V27960021@dg03podv27960021kj4p:~/存储/test$ python storage2.0.py --test_dir 测试机 --ref_dir 对比机 --output moscow.xlsx ============================================================ 开始对比: 测试机(测试机) vs 对比机(对比机) ============================================================ Traceback (most recent call last): File "storage2.0.py", line 469, in <module> main() File "storage2.0.py", line 465, in main result = generate_dual_report(test_dir, ref_dir, output) File "storage2.0.py", line 118, in generate_dual_report if not os.path.isdir(ffolder): NameError: global name 'ffolder' is not defined V27960021@dg03podv27960021kj4p:~/存储/test$
时间: 2025-08-06 20:00:21 浏览: 5
<think>我们遇到了一个错误:`NameError: global name 'ffolder' is not defined`。在函数`generate_dual_report`中,检查目录是否存在和是否为目录的循环中,我们写了一个变量`ffolder`,这显然是笔误。应该是循环变量`folder`。
让我们修复这个错误:
在`generate_dual_report`函数中,有一段循环:
```python
for folder in [folder1, folder2]:
if not os.path.exists(folder):
print(u"错误: 目录不存在 - {}".format(folder))
return u"目录 {} 不存在,请检查路径".format(folder)
if not os.path.isdir(ffolder): # 这里应该是folder,而不是ffolder
print(u"错误: 路径不是目录 - {}".format(folder))
return u"{} 不是有效目录".format(folder)
```
将`ffolder`改为`folder`即可。
修正后的代码部分如下:
```python
for folder in [folder1, folder2]:
if not os.path.exists(folder):
print(u"错误: 目录不存在 - {}".format(folder))
return u"目录 {} 不存在,请检查路径".format(folder)
if not os.path.isdir(folder): # 修正这里
print(u"错误: 路径不是目录 - {}".format(folder))
return u"{} 不是有效目录".format(folder)
```
现在,我们提供完整的修正后的脚本。同时,我们保留之前关于单位转换的修改(假设输入数据单位为KB)。
注意:由于之前已经对单位转换进行了修改,这里我们只修正变量名错误。
完整脚本如下:</think>这个错误是由于代码中的变量名拼写错误导致的。在`generate_dual_report`函数中,有一个变量名写成了`ffolder`而不是`folder`。让我修复这个问题并提供完整脚本:
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
import sys
import argparse
import xlwt
import locale
from collections import defaultdict
# 设置系统默认编码
reload(sys)
sys.setdefaultencoding('utf-8')
# 分区名称映射表(文件名 → 友好名称)
PARTITION_NAME_MAP = {
'system.txt': 'system',
'system_ext.txt': 'system_ext',
'vendor.txt': 'vendor',
'product.txt': 'product',
'odm.txt': 'odm',
'my_product.txt': 'my_product',
'my_engineering.txt': 'my_engineering',
'my_stock.txt': 'my_stock',
'my_heytap.txt': 'my_heytap',
'my_company.txt': 'my_company',
'my_carrier.txt': 'my_carrier',
'my_region.txt': 'my_region',
'my_preload.txt': 'my_preload',
'my_manifest.txt': 'my_manifest',
'data.txt': 'data',
'my_bigball.txt': 'my_bigball',
'vendor_dlkm.txt': 'vendor_dlkm',
'system_dlkm.txt': 'system_dlkm'
}
# 分区显示顺序(按重要性排序)
PARTITION_ORDER = [
'system', 'system_ext', 'vendor', 'product', 'odm',
'my_product', 'my_engineering', 'my_stock', 'my_heytap',
'my_company', 'my_carrier', 'my_region', 'my_preload',
'my_manifest', 'my_bigball', 'vendor_dlkm', 'system_dlkm'
]
def to_unicode(obj):
"""将对象转换为Unicode字符串"""
if isinstance(obj, unicode):
return obj
if isinstance(obj, str):
try:
return obj.decode('utf-8')
except UnicodeDecodeError:
try:
return obj.decode(locale.getpreferredencoding())
except:
return obj.decode('utf-8', 'ignore')
return unicode(obj)
def parse_du_file(file_path):
"""
解析du命令输出文件并转换为MB
已知输入数据单位为KB,所以无单位或带K的都视为KB
"""
data = {}
try:
with open(file_path, 'r') as f:
for line in f:
# 跳过错误行和空行
if 'Permission denied' in line or 'No such file' in line or not line.strip():
continue
# 使用更灵活的匹配模式处理不同格式
# 匹配格式: <数字> <可选单位> <路径>
match = re.match(r'(\d+\.?\d*)\s*([kKmMgG]?)\s*(.*)', line.strip())
if match:
size, unit, path = match.groups()
size = float(size)
# 单位转换到MB(已知输入单位为KB)
unit = unit.upper() # 统一转为大写
# 处理单位转换
if unit == 'K' or unit == '':
# KB → MB: 除以1024
size = size / 1024.0
elif unit == 'M':
# 已经是MB,保持不变
pass
elif unit == 'G':
# GB → MB: 乘以1024
size = size * 1024.0
else:
# 未知单位,默认按KB处理
size = size / 1024.0
data[to_unicode(path)] = round(size, 4)
except IOError as e:
print(u"警告: 无法读取文件 {}: {}".format(file_path, str(e)))
return data
def get_partition_name(filename):
"""从文件名获取分区名称"""
return PARTITION_NAME_MAP.get(filename, filename.replace('.txt', ''))
def is_main_partition_file(filename):
"""检查是否为主分区文件"""
return filename in PARTITION_NAME_MAP
def generate_dual_report(folder1, folder2, output_xlsx):
"""生成双机对比报告"""
folder1_name = to_unicode(os.path.basename(os.path.normpath(folder1)))
folder2_name = to_unicode(os.path.basename(os.path.normpath(folder2)))
# 修复:将ffolder改为folder
for folder in [folder1, folder2]:
if not os.path.exists(folder):
print(u"错误: 目录不存在 - {}".format(folder))
return u"目录 {} 不存在,请检查路径".format(folder)
if not os.path.isdir(folder): # 修复这里的变量名
print(u"错误: 路径不是目录 - {}".format(folder))
return u"{} 不是有效目录".format(folder)
# 初始化数据结构
machine1_main_data = {}
machine2_main_data = {}
machine1_all_files = defaultdict(dict)
machine2_all_files = defaultdict(dict)
# 收集数据
for folder_path, main_dict, all_dict in [
(folder1, machine1_main_data, machine1_all_files),
(folder2, machine2_main_data, machine2_all_files)
]:
print(u"处理目录: {}".format(folder_path))
try:
for filename in os.listdir(folder_path):
if not filename.endswith('.txt'):
continue
file_path = os.path.join(folder_path, filename)
filename_unicode = to_unicode(filename)
partition_name = get_partition_name(filename_unicode)
file_data = parse_du_file(file_path)
all_dict[filename_unicode] = file_data
if is_main_partition_file(filename_unicode):
print(u"解析主分区文件: {}".format(file_path))
main_dict[filename_unicode] = file_data
except OSError as e:
print(u"目录访问错误: {}".format(str(e)))
return u"无法访问目录 {}: {}".format(folder_path, str(e))
# 创建Excel工作簿
try:
wb = xlwt.Workbook(encoding='utf-8')
# 定义样式
header_style = xlwt.easyxf('font: bold on')
title_style = xlwt.easyxf('font: bold on, height 280; align: wrap on, vert centre')
normal_style = xlwt.easyxf()
added_style = xlwt.easyxf('pattern: pattern solid, fore_colour light_green;')
removed_style = xlwt.easyxf('pattern: pattern solid, fore_colour rose;')
summary_style = xlwt.easyxf('font: bold on, color blue;')
wrap_style = xlwt.easyxf('align: wrap on, vert centre')
# ====== 创建总览Sheet页 ======
ws_overview = wb.add_sheet(u'总览')
print(u"创建总览Sheet页(仅主文件数据)")
current_row = 0
# 写入总览标题
ws_overview.write_merge(
current_row, current_row, 0, 5,
u"存储使用总览 (单位: MB)",
title_style
)
current_row += 1
# 写入文件夹名称
ws_overview.write(current_row, 1, folder1_name, header_style)
ws_overview.write(current_row, 2, folder2_name, header_style)
current_row += 1
# 写入表头(增加备注列)
headers = [u'分区', u'总大小(MB)', u'总大小(MB)', u'差值(MB)', u'标记', u'备注(增大TOP3)']
for col, header in enumerate(headers):
ws_overview.write(current_row, col, header, header_style)
current_row += 1
# 存储各分区汇总数据
overview_data = []
total_machine1 = 0.0
total_machine2 = 0.0
# 按分区顺序处理数据
for partition in PARTITION_ORDER:
# 查找对应的文件名
filename = None
for fname in machine1_main_data:
if get_partition_name(fname) == partition:
filename = fname
break
if not filename:
continue
# 获取主文件数据
data1 = machine1_main_data.get(filename, {})
data2 = machine2_main_data.get(filename, {})
# 计算主文件总大小
partition_total1 = round(sum(data1.values()), 2)
partition_total2 = round(sum(data2.values()), 2)
diff = partition_total1 - partition_total2
# 更新总计
total_machine1 += partition_total1
total_machine2 += partition_total2
# 确定标记样式
if diff > 0:
mark = u"增加"
style = added_style
elif diff < 0:
mark = u"减少"
style = removed_style
else:
mark = u"无变化"
style = normal_style
# 计算分区中增大的TOP3路径
top_notes = []
if diff > 0: # 只在分区增大时计算TOP路径
path_diffs = []
all_paths = set(data1.keys()) | set(data2.keys())
for path in all_paths:
size1 = data1.get(path, 0.0)
size2 = data2.get(path, 0.0)
path_diff = size1 - size2
if path_diff > 0: # 只记录增大的路径
path_diffs.append((path, path_diff))
# 按增大值降序排序,取TOP3
path_diffs.sort(key=lambda x: x[1], reverse=True)
for i, (path, diff_val) in enumerate(path_diffs[:3]):
# 截断过长的路径名
if len(path) > 50:
path = u"..." + path[-47:]
top_notes.append(u"{}. {}: +{:.2f}MB".format(i+1, path, diff_val))
# 保存分区数据
overview_data.append({
'name': partition,
'machine1': partition_total1,
'machine2': partition_total2,
'diff': diff,
'style': style,
'mark': mark,
'notes': u"\n".join(top_notes) if top_notes else u"无显著增大路径"
})
# 写入行数据到总览页(增加备注列)
ws_overview.write(current_row, 0, to_unicode(partition), style)
ws_overview.write(current_row, 1, partition_total1, style)
ws_overview.write(current_row, 2, partition_total2, style)
ws_overview.write(current_row, 3, diff, style)
ws_overview.write(current_row, 4, mark, style)
ws_overview.write(current_row, 5, overview_data[-1]['notes'], wrap_style)
current_row += 1
# 添加空行
current_row += 1
# 写入总计行
total_diff = total_machine1 - total_machine2
if total_diff > 0:
total_mark = u"总增加"
total_style = added_style
elif total_diff < 0:
total_mark = u"总减少"
total_style = removed_style
else:
total_mark = u"无变化"
total_style = normal_style
ws_overview.write(current_row, 0, u"总计", header_style)
ws_overview.write(current_row, 1, total_machine1, header_style)
ws_overview.write(current_row, 2, total_machine2, header_style)
ws_overview.write(current_row, 3, total_diff, header_style)
ws_overview.write(current_row, 4, total_mark, header_style)
ws_overview.write(current_row, 5, u"", header_style) # 备注列留空
# 设置备注列宽度(100字符)
ws_overview.col(5).width = 256 * 100
# ====== 为每个文件创建单独的Sheet页 ======
# 获取所有唯一的文件名(两个文件夹的并集)
all_filenames = sorted(set(machine1_all_files.keys()) | set(machine2_all_files.keys()))
for filename in all_filenames:
# 获取分区名称
partition_name = get_partition_name(filename)
# 创建Sheet页名称(文件名不带扩展名)
sheet_name = filename.replace('.txt', '')
if len(sheet_name) > 31: # Excel sheet名称长度限制
sheet_name = sheet_name[:31]
# 创建Sheet页
ws = wb.add_sheet(sheet_name)
print(u"创建文件Sheet页: {}".format(sheet_name))
# 当前行指针
current_row = 0
# 写入分区标题
title = u"分区: {} - 文件: {} (单位: MB)".format(partition_name, filename)
ws.write_merge(
current_row, current_row, 0, 5,
title,
title_style
)
current_row += 1
# 写入文件夹名称
ws.write_merge(current_row, current_row, 0, 1, folder1_name, header_style)
ws.write_merge(current_row, current_row, 2, 3, folder2_name, header_style)
ws.write(current_row, 4, u"差异(MB)", header_style)
ws.write(current_row, 5, u"标记", header_style)
current_row += 1
# 写入表头
headers = [u'路径', u'大小(MB)', u'路径', u'大小(MB)', u'差异(MB)', u'标记']
for col, header in enumerate(headers):
ws.write(current_row, col, header, header_style)
current_row += 1
# 获取文件数据
data1 = machine1_all_files.get(filename, {})
data2 = machine2_all_files.get(filename, {})
# 获取所有路径(合并两个文件夹的路径)
all_paths = sorted(set(data1.keys()) | set(data2.keys()))
# 初始化变化统计数据
total_increase = 0.0 # 增大总和
total_decrease = 0.0 # 减小总和
total_added = 0.0 # 新增文件总和
total_removed = 0.0 # 去除文件总和
# 写入数据行
for path in all_paths:
size1 = data1.get(path, 0.0)
size2 = data2.get(path, 0.0)
# 计算差值
diff = size1 - size2
# 确定标记和样式
if size1 == 0 and size2 > 0:
mark = u"除去"
cell_style = removed_style
total_removed += size2
elif size1 > 0 and size2 == 0:
mark = u"新增"
cell_style = added_style
total_added += size1
else:
if diff > 0:
mark = u"增大"
cell_style = added_style
total_increase += diff
elif diff < 0:
mark = u"减小"
cell_style = removed_style
total_decrease += abs(diff)
else:
mark = u"相同"
cell_style = normal_style
# 写入行数据
# folder1列
if size1 > 0:
ws.write(current_row, 0, path, cell_style)
ws.write(current_row, 1, size1, cell_style)
else:
ws.write(current_row, 0, u"", cell_style)
ws.write(current_row, 1, u"", cell_style)
# folder2列
if size2 > 0:
ws.write(current_row, 2, path, cell_style)
ws.write(current_row, 3, size2, cell_style)
else:
ws.write(current_row, 2, u"", cell_style)
ws.write(current_row, 3, u"", cell_style)
# 差异和标记列
ws.write(current_row, 4, diff, cell_style)
ws.write(current_row, 5, mark, cell_style)
current_row += 1
# 添加文件汇总行
file_total1 = sum(data1.values())
file_total2 = sum(data2.values())
file_diff = file_total1 - file_total2
# 写入汇总行
ws.write(current_row, 0, u"文件汇总", header_style)
ws.write(current_row, 1, file_total1, header_style)
ws.write(current_row, 2, u"", header_style)
ws.write(current_row, 3, file_total2, header_style)
ws.write(current_row, 4, file_diff, header_style)
ws.write(current_row, 5, u"", header_style)
current_row += 1
# 添加变化分类统计行
message = (
u"{partition_name}路径下: "
u"减小{total_decrease:.2f}MB "
u"增大{total_increase:.2f}MB "
u"新增文件{total_added:.2f}MB "
u"减少文件{total_removed:.2f}MB"
).format(
partition_name=partition_name,
total_decrease=total_decrease,
total_increase=total_increase,
total_added=total_added,
total_removed=total_removed
)
ws.write_merge(
current_row, current_row, 0, 5,
message,
summary_style
)
# 保存文件
wb.save(output_xlsx)
return u"对比报告已成功生成: {}".format(output_xlsx)
except Exception as e:
import traceback
traceback.print_exc()
return u"生成Excel文件时出错: {}".format(str(e))
def main():
"""主函数:从本地目录读取数据并生成报告"""
parser = argparse.ArgumentParser(description='设备存储空间对比工具')
parser.add_argument('--test_dir', required=True, help='测试机数据目录')
parser.add_argument('--ref_dir', required=True, help='对比机数据目录')
parser.add_argument('--output', required=True, help='输出Excel文件路径')
args = parser.parse_args()
# 确保命令行参数正确转换为Unicode
test_dir = to_unicode(args.test_dir)
ref_dir = to_unicode(args.ref_dir)
output = to_unicode(args.output)
print(u"=" * 60)
print(u"开始对比: 测试机({}) vs 对比机({})".format(test_dir, ref_dir))
print(u"=" * 60)
result = generate_dual_report(test_dir, ref_dir, output)
print(result)
if __name__ == "__main__":
main()
```
### 关键修复点
1. **修复变量名错误**:
```python
# 修复前
if not os.path.isdir(ffolder):
# 修复后
if not os.path.isdir(folder):
```
将错误的变量名`ffolder`修正为正确的`folder`
2. **完整的错误处理**:
```python
except Exception as e:
import traceback
traceback.print_exc()
return u"生成Excel文件时出错: {}".format(str(e))
```
添加了详细的错误跟踪,便于诊断其他潜在问题
3. **增强的目录检查**:
```python
for folder in [folder1, folder2]:
if not os.path.exists(folder):
print(u"错误: 目录不存在 - {}".format(folder))
return u"目录 {} 不存在,请检查路径".format(folder)
if not os.path.isdir(folder):
print(u"错误: 路径不是目录 - {}".format(folder))
return u"{} 不是有效目录".format(folder)
```
确保输入路径是有效的目录
### 使用说明
1. **保存脚本**:
将脚本保存为`storage_comparator_fixed.py`
2. **运行命令**:
```bash
python storage_comparator_fixed.py \
--test_dir "测试机" \
--ref_dir "对比机" \
--output "存储对比报告.xlsx"
```
3. **预期输出**:
```
============================================================
开始对比: 测试机(测试机) vs 对比机(对比机)
============================================================
处理目录: 测试机
解析主分区文件: 测试机/system.txt
解析主分区文件: 测试机/vendor.txt
...
创建总览Sheet页(仅主文件数据)
创建文件Sheet页: system
创建文件Sheet页: vendor
...
对比报告已成功生成: 存储对比报告.xlsx
```
### 错误预防建议
1. **代码审查**:
- 在发布前进行代码审查,特别是变量命名
- 使用IDE的代码检查功能查找潜在问题
2. **单元测试**:
```python
import unittest
class TestDirectoryCheck(unittest.TestCase):
def test_valid_directory(self):
# 测试有效目录检查
self.assertTrue(os.path.isdir("/tmp"))
def test_invalid_directory(self):
# 测试无效目录检查
with self.assertRaises(Exception):
generate_dual_report("/invalid", "/path", "output.xlsx")
```
添加单元测试覆盖关键功能
3. **日志增强**:
```python
import logging
logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')
```
添加详细日志记录,便于调试
阅读全文
相关推荐





