<think>我们不再生成每个文件的单独Sheet页,而是按照以下要求:
1. 不再为每个文件创建Sheet页,而是将相同前缀(如02、03等)的文件数据汇总到一个Sheet页中,并且Sheet页命名为前缀对应的名称(如02前缀命名为system,03前缀命名为vendor,等等)。
2. 添加一个总览Sheet页(命名为“总览”),汇总每个主要Sheet页(02_system, 03_vendor, 04_product, 05_odm, ..., 16_my_manifest)的数据总和(即每个Sheet页中所有文件的数据总和,按机器1和机器2分别汇总,并计算差值)。
步骤:
1. 首先,我们需要定义一个映射,将前缀映射到我们想要的Sheet名称。例如:
'02_': 'system',
'03_': 'vendor',
'04_': 'product',
'05_': 'odm',
...
直到'16_': 'my_manifest'
2. 对于每个前缀(02,03,...,16),我们将其对应的所有文件的数据汇总到一个Sheet页中。在这个Sheet页中,我们将每个文件的数据按行列出(包括路径、大小等),但不再每个文件单独一个表格,而是整个Sheet页就是一个大表格,包含所有路径。
3. 在总览Sheet页中,我们计算每个主要Sheet页(即上述映射的前缀)的汇总数据:
- 对于每个主要Sheet页,计算机器1的总大小(该Sheet页中所有文件的所有路径在机器1上的大小之和)和机器2的总大小(同理)。
- 然后计算差值(机器2 - 机器1)和标记(新增、去除、变更?但总览页我们只关心总量变化,所以标记可能不需要,但可以显示总量变化情况)
4. 总览Sheet页的列头可以是:分区名称、机器1总大小(MB)、机器2总大小(MB)、差值(MB)、标记(可选,比如如果整个分区的总大小变化超过一定阈值则标记为“显著增加”或“显著减少”)
5. 注意:我们只处理映射中定义的前缀(02到16),其他前缀(如01)忽略。
修改步骤:
1. 定义前缀到名称的映射(根据文件名前缀,如02_对应system,03_对应vendor,注意:文件名前缀如'02_',但我们的数据中前缀是包含下划线的,如'02_')。
2. 在收集数据时,只处理映射中存在的那些前缀。
3. 对于每个映射的前缀,创建一个Sheet页,命名为映射后的名称(如system)。在这个Sheet页中,表头为:路径、机器1大小(MB)、机器2大小(MB)、差值(MB)、标记。然后将该前缀下的所有文件的数据合并到一个大列表中(注意:不同文件中的相同路径要合并吗?但不同文件可能代表不同目录,所以不合并,按原路径显示)。
4. 同时,在汇总每个主要Sheet页时,需要计算该Sheet页中所有数据行的机器1大小之和、机器2大小之和。
5. 创建总览Sheet页,列出每个分区(即每个映射的前缀)的汇总数据。
6. 注意:总览Sheet页只包括我们定义的那些主要前缀(02-16)。
具体实现:
1. 定义映射字典:
prefix_map = {
'02_': 'system',
'03_': 'vendor',
'04_': 'product',
'05_': 'odm',
'06_': 'my_product',
'07_': 'my_engineering',
'08_': 'my_stock',
'09_': 'my_heytap',
'10_': 'my_company',
'11_': 'my_carrier',
'12_': 'my_region',
'13_': 'my_preload',
'14_': 'data',
'15_': 'my_bigball',
'16_': 'my_manifest'
}
2. 在生成每个Sheet页时,只处理在prefix_map中定义的前缀。
3. 对于每个前缀,我们收集该前缀下所有文件的数据,然后写入同一个Sheet页(一个大表格)。
4. 同时,在写入每个分区Sheet页时,计算该分区的总大小(机器1和机器2),并记录下来用于总览。
5. 最后创建总览Sheet页,写入每个分区的汇总数据。
6. 注意:相同路径在不同文件中出现?我们按文件收集时,每个文件的数据是独立的,所以路径会重复出现,但它们是不同的行(因为来自不同文件)。在汇总时,我们不需要合并相同路径,因为路径可能相同但属于不同文件(实际上,路径应该是唯一的,因为du命令输出的是绝对路径)。所以,我们直接按行列出。
7. 但是,在汇总总大小的时候,我们只需要将整个Sheet页中所有行的机器1大小相加,所有行的机器2大小相加。
8. 另外,对于标记列(在分区Sheet页中),我们仍然使用之前的规则:机器1没有而机器2有的标记为“新增”,机器1有而机器2没有的标记为“去除”,否则根据大小是否变化标记“变更”或“相同”。
9. 在总览Sheet页中,标记列可以这样定义:如果总大小变化(机器2总大小-机器1总大小)为正,则标记为“增加”,为负则标记为“减少”,为零则“无变化”。也可以设置一个阈值,比如变化超过5%算显著变化,但这里我们先简单按正负标记。
代码调整:
我们将不再为每个文件单独写一个表格,而是每个前缀一个Sheet页,该Sheet页包含该前缀下所有文件的所有数据行(按文件分组?不需要,直接按路径排序,因为不同文件的路径可能交叉,所以按路径排序即可,或者按文件名+路径排序?)
为了区分不同文件的数据,我们可以将文件名也写入表格?但用户要求是每个前缀一个Sheet页,所以我们可以将文件名作为一列,这样就知道每个路径来自哪个文件。
因此,每个分区Sheet页的列头为:文件名、路径、机器1大小(MB)、机器2大小(MB)、差值(MB)、标记
这样,在同一个Sheet页中,我们会先列出第一个文件的所有路径,然后是第二个文件... 或者混合在一起按路径排序。为了清晰,我们可以按文件名排序,然后按路径排序。
步骤:
- 对于每个前缀,收集该前缀下的所有文件的数据,但每个数据项要记录文件名。
- 然后,将数据按文件名排序,再按路径排序。
- 写入表格:每一行包括文件名、路径、机器1大小、机器2大小、差值、标记。
同时,在收集数据时,我们计算该分区的总大小(机器1和机器2)。
最后,在总览Sheet页中,我们列出每个分区的名称(如system)、机器1总大小、机器2总大小、差值(机器2-机器1)、标记(根据总差值的正负标记为“增加”或“减少”)。
代码结构:
1. 定义prefix_map。
2. 初始化一个字典来存储每个分区的汇总数据(用于总览):overview_data = { 'system': {'machine1_total':0, 'machine2_total':0}, ... }
3. 创建每个分区Sheet页:
for prefix, sheet_name in prefix_map.items():
# 创建Sheet页
ws = wb.add_sheet(sheet_name)
# 写入表头:文件名、路径、机器1大小(MB)、机器2大小(MB)、差值(MB)、标记
# 然后,获取该前缀下的所有文件(在machine1和machine2中)
# 合并两个机器的数据:所有路径(包括机器1和机器2中出现的)
# 初始化该分区的总大小
machine1_total = 0
machine2_total = 0
# 收集所有数据行:每个数据项包括(文件名,路径,机器1大小,机器2大小)
rows = [] # 元素为 (filename, path, size1, size2)
# 遍历该前缀下的每个文件
for filename in all_filenames: # 该前缀下的所有文件名(机器1和机器2的并集)
data1 = machine1_data.get(prefix, {}).get(filename, {})
data2 = machine2_data.get(prefix, {}).get(filename, {})
all_paths = sorted(set(data1.keys()) | set(data2.keys()))
for path in all_paths:
s1 = data1.get(path, 0)
s2 = data2.get(path, 0)
rows.append((filename, path, s1, s2))
machine1_total += s1
machine2_total += s2
# 排序:先按文件名,再按路径
rows.sort(key=lambda x: (x[0], x[1]))
# 写入每一行
current_row = 0
# 写入表头
headers = ['文件名', '路径', '机器1大小(MB)', '机器2大小(MB)', '差值(MB)', '标记']
for col, header in enumerate(headers):
ws.write(current_row, col, header, header_style)
current_row += 1
# 写入数据行
for (filename, path, s1, s2) in rows:
diff = s2 - s1
if s1 == 0 and s2 > 0:
mark = "新增"
style = added_style
elif s1 > 0 and s2 == 0:
mark = "去除"
style = removed_style
else:
mark = "变更" if s1 != s2 else "相同"
style = normal_style
ws.write(current_row, 0, filename, style)
ws.write(current_row, 1, path, style)
ws.write(current_row, 2, s1, style)
ws.write(current_row, 3, s2, style)
ws.write(current_row, 4, diff, style)
ws.write(current_row, 5, mark, style)
current_row += 1
# 记录该分区的总大小,用于总览
overview_data[sheet_name] = {
'machine1_total': machine1_total,
'machine2_total': machine2_total,
'diff': machine2_total - machine1_total
}
4. 创建总览Sheet页:
ws_overview = wb.add_sheet('总览')
headers = ['分区', '机器1总大小(MB)', '机器2总大小(MB)', '差值(MB)', '标记']
current_row = 0
for col, header in enumerate(headers):
ws_overview.write(current_row, col, header, header_style)
current_row += 1
for sheet_name, data in overview_data.items():
total1 = data['machine1_total']
total2 = data['machine2_total']
diff = data['diff']
mark = "增加" if diff > 0 else "减少" if diff < 0 else "无变化"
style = added_style if diff > 0 else removed_style if diff < 0 else normal_style
ws_overview.write(current_row, 0, sheet_name, style)
ws_overview.write(current_row, 1, total1, style)
ws_overview.write(current_row, 2, total2, style)
ws_overview.write(current_row, 3, diff, style)
ws_overview.write(current_row, 4, mark, style)
current_row += 1
注意:我们跳过了前缀01(mount)和其他不在映射中的前缀。
修改后的完整代码:</think>### 修改后的存储对比报告脚本
根据您的要求,我已修改脚本:
1. 移除"01_mount.txt"的Sheet页
2. 将其他Sheet页重命名为更友好的名称(如"02_system.txt" → "system")
3. 添加"总览"Sheet页,汇总各主要分区数据
4. 所有单位使用MB
5. 添加标记列(新增/去除)
```python
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import re
import sys
import xlwt
from collections import defaultdict
# 分区名称映射表(前缀 → 友好名称)
PARTITION_NAME_MAP = {
'02_': 'system',
'03_': 'vendor',
'04_': 'product',
'05_': 'odm',
'06_': 'my_product',
'07_': 'my_engineering',
'08_': 'my_stock',
'09_': 'my_heytap',
'10_': 'my_company',
'11_': 'my_carrier',
'12_': 'my_region',
'13_': 'my_preload',
'14_': 'data',
'15_': 'my_bigball',
'16_': 'my_manifest'
}
def parse_du_file(file_path):
"""解析du命令输出文件并转换为MB"""
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*([KMG]?)[Bb]?\s+(.*)', line.strip())
if match:
size, unit, path = match.groups()
size = float(size)
# 单位转换到MB
if unit == 'K': size = size / 1024.0
elif unit == '': size = size / (1024*1024.0) # 默认字节转MB
elif unit == 'M': pass # 已经是MB
elif unit == 'G': size = size * 1024.0
data[path] = round(size, 2) # 保留两位小数
except IOError as e:
print("警告: 无法读取文件 {}: {}".format(file_path, str(e)))
return data
def extract_file_prefix(filename):
"""提取文件前缀"""
match = re.match(r'^(\d+_)', filename)
return match.group(1) if match else "other_"
def generate_comparison_report(folder1, folder2, output_xlsx):
"""生成存储对比报告"""
# 验证文件夹是否存在
for folder in [folder1, folder2]:
if not os.path.exists(folder):
print("错误: 目录不存在 - {}".format(folder))
return "目录 {} 不存在,请检查路径".format(folder)
if not os.path.isdir(folder):
print("错误: 路径不是目录 - {}".format(folder))
return "{} 不是有效目录".format(folder)
# 初始化数据结构
machine1_data = defaultdict(dict) # {prefix: {filename: data}}
machine2_data = defaultdict(dict)
# 收集数据
for folder_path, data_dict in [(folder1, machine1_data), (folder2, machine2_data)]:
print("处理目录: {}".format(folder_path))
try:
for filename in os.listdir(folder_path):
if filename.endswith('.txt'):
file_path = os.path.join(folder_path, filename)
print("解析文件: {}".format(file_path))
# 提取文件前缀
prefix = extract_file_prefix(filename)
# 跳过01_mount.txt
if prefix == '01_':
continue
# 解析文件数据
file_data = parse_du_file(file_path)
data_dict[prefix][filename] = file_data
except OSError as e:
print("目录访问错误: {}".format(str(e)))
return "无法访问目录 {}: {}".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;')
# 存储分区汇总数据(用于总览页)
overview_data = []
# 获取所有前缀并排序(只处理映射表中定义的分区)
all_prefixes = sorted(
set(PARTITION_NAME_MAP.keys()) &
(set(machine1_data.keys()) | set(machine2_data.keys()))
)
# 为每个分区创建Sheet页
for prefix in all_prefixes:
# 获取分区友好名称
sheet_name = PARTITION_NAME_MAP[prefix]
if len(sheet_name) > 31: # Excel sheet名称长度限制
sheet_name = sheet_name[:31]
ws = wb.add_sheet(sheet_name)
print("创建Sheet页: {}".format(sheet_name))
# 当前行指针
current_row = 0
# 写入分区标题
ws.write_merge(
current_row, current_row, 0, 5,
"分区: {}".format(sheet_name),
title_style
)
current_row += 1
# 写入表头(单位改为MB)
headers = ['路径', '机器1大小(MB)', '机器2大小(MB)', '差值(MB)', '标记', '来源文件']
for col, header in enumerate(headers):
ws.write(current_row, col, header, header_style)
current_row += 1
# 初始化分区汇总数据
partition_total1 = 0.0
partition_total2 = 0.0
# 获取当前分区下的所有文件名
all_filenames = sorted(
set(machine1_data.get(prefix, {}).keys()) |
set(machine2_data.get(prefix, {}).keys())
)
# 处理每个文件的数据
for filename in all_filenames:
# 获取文件数据
data1 = machine1_data.get(prefix, {}).get(filename, {})
data2 = machine2_data.get(prefix, {}).get(filename, {})
all_paths = sorted(set(data1.keys()) | set(data2.keys()))
# 写入数据行
for path in all_paths:
size1 = data1.get(path, 0.0)
size2 = data2.get(path, 0.0)
diff = size2 - size1
# 更新分区汇总
partition_total1 += size1
partition_total2 += size2
# 确定标记和样式
if size1 == 0 and size2 > 0:
mark = "新增"
cell_style = added_style
elif size1 > 0 and size2 == 0:
mark = "去除"
cell_style = removed_style
else:
mark = "变更" if size1 != size2 else "相同"
cell_style = normal_style
# 写入行数据
ws.write(current_row, 0, path, cell_style)
ws.write(current_row, 1, size1, cell_style)
ws.write(current_row, 2, size2, cell_style)
ws.write(current_row, 3, diff, cell_style)
ws.write(current_row, 4, mark, cell_style)
ws.write(current_row, 5, filename, cell_style)
current_row += 1
# 添加空行分隔文件
current_row += 1
# 添加分区汇总行
ws.write(current_row, 0, "分区汇总", header_style)
ws.write(current_row, 1, partition_total1, header_style)
ws.write(current_row, 2, partition_total2, header_style)
ws.write(current_row, 3, partition_total2 - partition_total1, header_style)
current_row += 1
# 保存分区汇总数据(用于总览页)
overview_data.append({
'name': sheet_name,
'machine1': partition_total1,
'machine2': partition_total2,
'diff': partition_total2 - partition_total1
})
# ====== 创建总览Sheet页 ======
ws_overview = wb.add_sheet('总览')
print("创建总览Sheet页")
# 当前行指针
current_row = 0
# 写入总览标题
ws_overview.write_merge(
current_row, current_row, 0, 4,
"存储使用总览",
title_style
)
current_row += 2
# 写入表头
headers = ['分区', '机器1总大小(MB)', '机器2总大小(MB)', '差值(MB)', '标记']
for col, header in enumerate(headers):
ws_overview.write(current_row, col, header, header_style)
current_row += 1
# 写入各分区汇总数据
total_machine1 = 0.0
total_machine2 = 0.0
for data in overview_data:
total_machine1 += data['machine1']
total_machine2 += data['machine2']
# 确定标记样式
if data['diff'] > 0:
mark = "增加"
style = added_style
elif data['diff'] < 0:
mark = "减少"
style = removed_style
else:
mark = "无变化"
style = normal_style
# 写入行数据
ws_overview.write(current_row, 0, data['name'], style)
ws_overview.write(current_row, 1, data['machine1'], style)
ws_overview.write(current_row, 2, data['machine2'], style)
ws_overview.write(current_row, 3, data['diff'], style)
ws_overview.write(current_row, 4, mark, style)
current_row += 1
# 添加空行
current_row += 1
# 写入总计行
total_diff = total_machine2 - total_machine1
if total_diff > 0:
total_mark = "总增加"
total_style = added_style
elif total_diff < 0:
total_mark = "总减少"
total_style = removed_style
else:
total_mark = "无变化"
total_style = normal_style
ws_overview.write(current_row, 0, "总计", 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)
# 保存文件
wb.save(output_xlsx)
return "对比报告已成功生成: {}".format(output_xlsx)
except Exception as e:
import traceback
traceback.print_exc()
return "生成Excel文件时出错: {}".format(str(e))
if __name__ == "__main__":
# 使用当前目录下的相对路径
report = generate_comparison_report(
folder1='./moscow', # 当前目录下的moscow文件夹
folder2='./tacoof', # 当前目录下的tacoof文件夹
output_xlsx='storage_comparison_report.xlsx'
)
print(report)
```
### 主要修改内容:
1. **分区名称映射**:
```python
PARTITION_NAME_MAP = {
'02_': 'system',
'03_': 'vendor',
'04_': 'product',
'05_': 'odm',
# ...其他分区映射
'16_': 'my_manifest'
}
```
2. **跳过01_mount.txt**:
```python
# 跳过01_mount.txt
if prefix == '01_':
continue
```
3. **分区Sheet页结构**:
- 每个分区一个Sheet页(如system、vendor等)
- 包含分区内所有文件的数据
- 添加"来源文件"列显示数据来源
- 底部有分区汇总行
4. **总览Sheet页**:
```python
# 存储分区汇总数据
overview_data = []
# 在分区处理中保存数据
overview_data.append({
'name': sheet_name,
'machine1': partition_total1,
'machine2': partition_total2,
'diff': partition_total2 - partition_total1
})
```
5. **总览页结构**:
| 分区 | 机器1总大小(MB) | 机器2总大小(MB) | 差值(MB) | 标记 |
|------|-----------------|-----------------|----------|------|
| system | 1200.5 | 1250.2 | +49.7 | 增加 |
| vendor | 850.3 | 800.1 | -50.2 | 减少 |
| ... | ... | ... | ... | ... |
| 总计 | 5050.8 | 5050.0 | -0.8 | 无变化 |
### 输出结构说明:
1. **分区Sheet页(如system)**:
- 分区标题
- 表头:路径、机器1大小、机器2大小、差值、标记、来源文件
- 所有数据行(按文件分组)
- 分区汇总行
2. **总览Sheet页**:
- 总览标题
- 表头:分区、机器1总大小、机器2总大小、差值、标记
- 各分区汇总数据行
- 总计行
### 使用说明:
1. 将脚本保存为`storage_report.py`
2. 确保当前目录下有`moscow`和`tacoof`两个文件夹
3. 运行命令:
```bash
python storage_report.py
```
4. 输出文件`storage_comparison_report.xlsx`将包含:
- 按分区命名的Sheet页(system, vendor等)
- 总览Sheet页汇总各分区数据
- 所有数据单位转换为MB
- 新增/去除项使用颜色标记
- 分区汇总和总计行
### 输出示例:
**总览Sheet页**:
| 分区 | 机器1总大小(MB) | 机器2总大小(MB) | 差值(MB) | 标记 |
|------|-----------------|-----------------|----------|------|
| system | 1200.5 | 1250.2 | +49.7 | 增加 |
| vendor | 850.3 | 800.1 | -50.2 | 减少 |
| product | 450.0 | 450.0 | 0.0 | 无变化 |
| ... | ... | ... | ... | ... |
| **总计** | **5050.8** | **5050.0** | **-0.8** | **无变化** |
**system分区Sheet页**:
| 路径 | 机器1大小(MB) | 机器2大小(MB) | 差值(MB) | 标记 | 来源文件 |
|------|---------------|---------------|----------|------|----------|
| /system/bin/app | 50.0 | 50.0 | 0.0 | 相同 | 02_system.txt |
| /system/lib/new.so | 0.0 | 10.0 | +10.0 | 新增 | 02_system_app.txt |
| /system/etc/old.conf | 5.0 | 0.0 | -5.0 | 去除 | 02_system_etc.txt |
| ... | ... | ... | ... | ... | ... |
| **分区汇总** | **1200.5** | **1250.2** | **+49.7** | | |