Mac 系统依赖信息收集脚本模块化解析
该脚本核心功能是自动收集 macOS 系统中 Homebrew 安装包和 Python 第三方包的关键信息(名称、占用空间、功能描述),并整理生成结构化的 Excel 报告,整体采用模块化设计,各模块职责清晰、可复用性强,适配 macOS 环境的依赖管理场景。
一、脚本整体架构
脚本按 “功能职责” 拆分为工具函数模块、Brew 依赖收集模块、Python 包收集模块、Excel 报告生成模块、主程序入口模块五大核心模块,整体架构如下:
├── 工具函数模块:字节数格式化(通用能力)
├── Brew依赖收集模块:获取brew包名称/大小/描述
├── Python包收集模块:获取Python包名称/版本/大小/描述
├── Excel报告生成模块:数据写入Excel并格式化样式
└── 主程序入口模块:流程调度+依赖检查+异常处理
二、各模块功能与核心代码解析
模块 1:工具函数模块(通用能力封装)
核心职责:提供全局复用的工具函数,解决 “字节数转易读单位” 的通用需求,与业务逻辑解耦。
def get_human_readable_size(size_bytes):
"""将字节数转换为易读的单位(B/KB/MB/GB/TB),适配不同大小的依赖包展示"""
if size_bytes == 0:
return "0 B"
size_names = ("B", "KB", "MB", "GB", "TB")
# 按二进制位数计算单位层级(1024进制)
i = int(len(bin(size_bytes)) // 10)
p = 1024 ** i
s = round(size_bytes / p, 2)
return f"{s} {size_names[i]}"
关键说明:
- 适配 0 字节、大文件(GB 级)等边界场景,避免计算异常;
- 采用 1024 进制(符合存储行业标准),保留 2 位小数,兼顾精度与可读性;
- 函数无外部依赖,可直接复用至其他需要格式化文件大小的场景。
模块 2:Brew 依赖收集模块(系统包信息采集)
核心职责:针对 macOS 专属的 Homebrew 包管理器,采集已安装包的名称、磁盘占用大小、功能描述,是脚本的核心业务模块之一。
import subprocess
import json
import os
import shutil
def get_brew_packages_info():
"""获取brew安装的包信息:名称、大小、作用"""
brew_packages = []
# 前置检查:验证brew是否安装(避免无效执行)
if not shutil.which("brew"):
print("未检测到Homebrew,请先安装brew")
return brew_packages
try:
# 步骤1:获取所有已安装的brew formula包名称
list_result = subprocess.check_output(
["brew", "list", "--formula"],
text=True,
stderr=subprocess.DEVNULL # 屏蔽错误输出,避免干扰
).splitlines()
for pkg_name in list_result:
pkg_name = pkg_name.strip()
if not pkg_name:
continue
# 步骤2:通过brew info获取包的描述信息(JSON格式便于解析)
info_result = subprocess.check_output(
["brew", "info", "--json=v1", pkg_name],
text=True,
stderr=subprocess.DEVNULL
)
pkg_info = json.loads(info_result)[0]
pkg_desc = pkg_info.get("desc", "无描述信息")
# 步骤3:遍历包的所有文件,计算总磁盘占用大小
files_result = subprocess.check_output(
["brew", "ls", "--verbose", pkg_name],
text=True,
stderr=subprocess.DEVNULL
).splitlines()
total_size = 0
for file_path in files_result:
file_path = file_path.strip()
# 排除软链接,仅计算实际文件大小
if os.path.exists(file_path) and not os.path.islink(file_path):
total_size += os.path.getsize(file_path)
# 封装数据结构,统一格式
brew_packages.append({
"名称": pkg_name,
"占用大小": get_human_readable_size(total_size),
"作用": pkg_desc
})
except subprocess.CalledProcessError as e:
print(f"获取brew包信息时出错: {e}")
except Exception as e:
print(f"处理brew包信息异常: {e}")
return brew_packages
关键说明:
- 前置校验:通过
shutil.which("brew")检查 brew 是否安装,避免后续命令执行失败; - 多命令协作:
brew list --formula:获取已安装包列表(仅公式包,排除 cask);brew info --json=v1:以 JSON 格式输出包信息,解析更稳定;brew ls --verbose:列出包的所有安装文件,用于计算总大小;
- 精度保障:排除软链接(
not os.path.islink),避免重复计算磁盘占用; - 异常处理:捕获
subprocess.CalledProcessError(命令执行失败)和通用异常,保证单个包解析失败不影响整体流程。
模块 3:Python 包收集模块(Python 依赖采集)
核心职责:采集当前 Python 环境中已安装包的名称(含版本)、磁盘占用大小、功能描述,适配不同 Python 环境(系统 Python / 虚拟环境)。
import pkg_resources
import sys
from pathlib import Path
def get_python_packages_info():
"""获取Python已安装包信息:名称、大小、作用"""
python_packages = []
try:
# 步骤1:获取当前环境所有已安装的Python包
installed_packages = pkg_resources.working_set
for pkg in installed_packages:
pkg_name = pkg.project_name
pkg_version = pkg.version
# 步骤2:定位包的安装路径,计算总大小
# 兼容不同包的路径命名规则(小写/下划线替换横线)
pkg_location = Path(pkg.location) / pkg_name.replace("-", "_").lower()
if not pkg_location.exists():
pkg_location = Path(pkg.location) / pkg_name
total_size = 0
if pkg_location.exists():
if pkg_location.is_file(): # 单文件包(如egg-info)
total_size = pkg_location.stat().st_size
else: # 目录包,递归计算所有文件大小
for file in pkg_location.rglob("*"):
if file.is_file():
total_size += file.stat().st_size
# 步骤3:通过pip show获取包的描述信息
try:
show_result = subprocess.check_output(
[sys.executable, "-m", "pip", "show", pkg_name],
text=True,
stderr=subprocess.DEVNULL
)
pkg_desc = "无描述信息"
for line in show_result.splitlines():
if line.startswith("Description:"):
pkg_desc = line.split(":", 1)[1].strip()
break
except:
pkg_desc = "无法获取描述"
# 封装数据,包含版本号提升信息完整性
python_packages.append({
"名称": f"{pkg_name} ({pkg_version})",
"占用大小": get_human_readable_size(total_size),
"作用": pkg_desc
})
except Exception as e:
print(f"获取Python包信息异常: {e}")
return python_packages
关键说明:
- 环境适配:通过
pkg_resources.working_set获取当前 Python 环境的包,适配虚拟环境 / 全局环境; - 路径兼容:处理包名的 “横线 / 下划线”“大小写” 差异(如
requestsvsRequests),避免路径查找失败; - 大小计算:区分单文件包和目录包,递归计算目录包的所有文件大小,保证精度;
- 描述获取:通过
pip show命令获取描述,失败时默认 “无法获取描述”,不中断流程; - 版本标注:包名称中包含版本号(如
requests (2.31.0)),提升报告的实用性。
模块 4:Excel 报告生成模块(数据可视化输出)
核心职责:将采集到的 Brew/Python 包数据写入 Excel 文件,格式化单元格样式,提升报告可读性。
import openpyxl
from openpyxl.styles import Font, Alignment
import os
def write_to_excel(brew_data, python_data, output_file="mac_dependencies_report.xlsx"):
"""将数据写入Excel文件,分sheet展示Brew/Python依赖"""
# 创建新工作簿
wb = openpyxl.Workbook()
# 删除默认的Sheet1,避免冗余
wb.remove(wb.active)
# 处理Brew数据:独立Sheet展示
if brew_data:
ws_brew = wb.create_sheet(title="Brew依赖")
# 设置表头样式(加粗+居中)
headers = ["名称", "占用大小", "作用"]
for col, header in enumerate(headers, 1):
cell = ws_brew.cell(row=1, column=col, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal="center")
# 写入数据行
for row, pkg in enumerate(brew_data, 2):
ws_brew.cell(row=row, column=1, value=pkg["名称"])
ws_brew.cell(row=row, column=2, value=pkg["占用大小"])
ws_brew.cell(row=row, column=3, value=pkg["作用"])
# 调整列宽,适配内容展示
ws_brew.column_dimensions["A"].width = 20
ws_brew.column_dimensions["B"].width = 15
ws_brew.column_dimensions["C"].width = 60
# 处理Python数据:独立Sheet展示
if python_data:
ws_python = wb.create_sheet(title="Python包")
# 表头样式与Brew Sheet保持一致
headers = ["名称", "占用大小", "作用"]
for col, header in enumerate(headers, 1):
cell = ws_python.cell(row=1, column=col, value=header)
cell.font = Font(bold=True)
cell.alignment = Alignment(horizontal="center")
# 写入数据行
for row, pkg in enumerate(python_data, 2):
ws_python.cell(row=row, column=1, value=pkg["名称"])
ws_python.cell(row=row, column=2, value=pkg["占用大小"])
ws_python.cell(row=row, column=3, value=pkg["作用"])
# 调整列宽(Python包名含版本,需更宽)
ws_python.column_dimensions["A"].width = 30
ws_python.column_dimensions["B"].width = 15
ws_python.column_dimensions["C"].width = 60
# 保存文件并输出绝对路径,便于用户查找
wb.save(output_file)
print(f"报告已生成:{os.path.abspath(output_file)}")
关键说明:
- 分 Sheet 设计:Brew 和 Python 数据分别放在不同 Sheet,结构清晰;
- 样式优化:表头加粗 + 居中,提升可读性;
- 列宽适配:根据内容特点调整列宽(如 “作用” 列 60 字符宽度,避免内容截断);
- 容错处理:仅当数据非空时创建对应 Sheet,避免生成空 Sheet;
- 路径提示:输出文件绝对路径,用户可直接复制打开。
模块 5:主程序入口模块(流程调度与依赖检查)
核心职责:作为脚本的入口,调度各模块执行流程,检查必要依赖,处理整体异常。
def main():
"""主函数:调度数据采集→报告生成全流程"""
print("开始收集依赖信息...")
# 步骤1:采集数据
brew_info = get_brew_packages_info()
python_info = get_python_packages_info()
# 步骤2:生成Excel报告
write_to_excel(brew_info, python_info)
print("依赖信息收集完成!")
if __name__ == "__main__":
# 前置检查:确保openpyxl已安装,未安装则自动安装
try:
import openpyxl
except ImportError:
print("正在安装必要依赖包...")
subprocess.check_call([sys.executable, "-m", "pip", "install", "openpyxl"])
import openpyxl
main()
关键说明:
- 自动安装依赖:检测到
openpyxl未安装时,自动通过pip安装,降低用户使用门槛; - 流程简化:用户只需执行脚本,无需手动调用各模块,一键完成数据采集 + 报告生成;
- 用户反馈:输出执行进度提示(“开始收集”“完成”),提升使用体验;
- 入口防护:通过
if __name__ == "__main__"保证模块导入时不执行主流程,提升脚本复用性。
三、脚本核心设计亮点
- 模块化解耦:各模块职责单一(如工具函数仅做格式化,采集模块仅做数据获取),便于单独修改 / 扩展(如新增 npm 包采集模块);
- 环境适配:
- 适配 macOS 的 Homebrew 环境,前置检查 brew 是否安装;
- 适配不同 Python 环境,兼容包路径的命名差异;
- 容错性强:
- 单个包解析失败不影响整体流程;
- 屏蔽子进程错误输出,避免干扰用户;
- 自动安装缺失依赖,降低使用门槛;
- 用户友好:
- 易读的大小格式、结构化的 Excel 报告;
- 清晰的进度提示、绝对路径输出;
- 样式化的 Excel 单元格,提升报告可读性;
- 可复用性:各模块可单独导入使用(如
get_human_readable_size函数可复用至其他脚本,get_python_packages_info可单独采集 Python 包信息)。

6474

被折叠的 条评论
为什么被折叠?



