最可靠方案是用 os.walk() 递归遍历并累加文件大小,配合异常捕获跳过权限错误、inode 去重防硬链接重复计算、统一单位换算(如 gib=1024³)、超时控制与进度反馈,兼顾跨平台兼容性与精度要求。

用 os.walk() 遍历并累加文件大小最可靠
直接调用 os.stat() 获取单个文件大小没问题,但对整个目录,必须递归遍历所有子目录和文件。os.walk() 是标准库中唯一能稳定处理符号链接、权限异常、嵌套层级的方案。别用 pathlib.Path.rglob("*")——它在遇到权限拒绝时会直接抛 PermissionError 中断,而 os.walk() 可以通过异常捕获跳过。
实操建议:
- 用
topdown=True(默认)避免进入无权限子目录前先触发错误 - 对每个
root, dirs, files元组,只对files列表中的项调用os.path.join(root, f)+os.path.getsize() - 遇到
OSError(如PermissionError或FileNotFoundError)时continue,不要break - 累计变量用
int,别用float,避免浮点精度误差影响阈值判断
阈值单位要统一换算,别混用 GiB/GB/MB
用户常把“10G”理解成 10 × 1024³ 字节(GiB),但系统工具(如 du -h)默认显示的是十进制 GB(10⁹ 字节)。Python 里没内置单位解析,必须手动转。
常见错误现象:if total_size > 10 * 1024 * 1024 * 1024 看似正确,但如果阈值来自配置文件字符串(如 "10GB"),不解析就直接比较会出错。
实操建议:
- 定义统一换算因子:比如
GiB = 1024**3,GB = 10**9,明确标注用途 - 若读取配置项(如 YAML/INI),先用正则提取数字和单位,再按约定映射:
{"GB": GB, "GiB": GiB, "MB": 1024**2} - 打印诊断信息时,用
round(total_size / GiB, 2)展示为 GiB,避免用户困惑
大目录下 os.walk() 卡住?加超时和进度反馈
os.walk() 本身不支持超时,遇到挂载失败的 NFS 目录或卡死的 symlink 可能无限阻塞。而且对 TB 级目录,用户不知道程序是否还在运行。
实操建议:
- 用
signal.alarm()(仅 Linux/macOS)或threading.Timer包裹遍历逻辑,超时后主动中断 - 每处理完 1000 个文件,打印一次当前累计大小(用
sys.stderr避免干扰 stdout 输出) - 跳过设备文件(
os.path.isdev())和 socket 文件,它们的getsize()可能返回 0 或异常 - 慎用
os.scandir()替代 —— 它虽快,但 Python 3.7+ 才稳定支持递归,且异常处理更复杂
跨平台注意 Windows 的硬链接与重解析点
Windows 下 NTFS 硬链接和目录交接点(junction)会导致重复计算:同一文件被多个路径引用,os.walk() 默认会分别计入。Linux 的硬链接也会被重复统计,但符号链接(symlink)默认不跟随(follow_symlinks=False)。
实操建议:
- Windows 上开启
follow_symlinks=False(默认),但需额外检查os.path.islink()并跳过,防止循环遍历 - 用
os.stat().st_ino和os.stat().st_dev组合去重:缓存已见过的(ino, dev)对,重复则跳过 - 不要依赖
os.path.realpath()去重——它在 junction 点上行为不稳定,且开销大 - 若业务允许,加开关参数控制是否去重,因为去重会增加内存占用(尤其海量小文件)
实际跑起来才发现,inode 去重逻辑在百万级文件目录里会让内存涨一倍,而跳过硬链接又可能漏算真实占用。阈值检测这事,真得看清楚你到底想测“磁盘占用”还是“路径下可见字节数”。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











