filecmp.dircmp默认不比内容,因其仅依赖文件名、大小和修改时间(mtime)做轻量级结构比对,避免I/O开销;需设shallow=False并配合filecmp.cmp(f1,f2,shallow=False)逐字节校验diff_files才能确认真实内容差异。

直接用 filecmp.dircmp 是最快路径,但默认行为会漏判内容差异——关键在 shallow=False 和手动校验 diff_files。
为什么 filecmp.dircmp 默认不比内容?
它只检查文件名、大小和修改时间(mtime),不读取字节。跨平台同步、编辑后保存、硬链接等场景下,mtime 可能不变,size 也可能巧合一致,导致 diff_files 为空,但实际内容已不同。
- 调用
filecmp.dircmp(a, b)后,d.diff_files列表只反映“结构级差异”,不是“真实内容差异” -
d.same_files同理,仅表示 size + mtime 相同,不代表内容一致 - 子目录差异藏在
d.subdirs字典里,需递归访问,不手动遍历就看不到嵌套变化
如何真正比内容?逐字节 or 哈希?
小文件(filecmp.cmp(f1, f2, shallow=False) 最简;大文件必须避免内存爆炸,改用分块哈希。
-
shallow=False强制逐字节比对,但会把两个文件全 load 进内存 —— 500MB 文件可能触发MemoryError - 更稳方案:用
hashlib.sha256()分块读(如每次 8192 字节),算哈希再比对,内存占用恒定 - 注意:不要用
os.path.getsize()+os.path.getmtime()自己拼判断逻辑,filecmp.dircmp的内部实现已优化路径遍历和 stat 批量调用
怎么忽略临时文件或 .git 目录?
filecmp.dircmp 支持 hide 和 ignore,但语义易混淆:两者都跳过匹配项,hide 是“隐藏显示”,ignore 是“完全不参与比对”。实际效果一样,选一个用就行。
立即学习“Python免费学习笔记(深入)”;
- 传入列表,如
ignore=['.git', '__pycache__', '*.tmp']—— 支持通配符,不支持正则 - 路径匹配基于 basename,不是完整路径,所以
'temp'会同时过滤/a/temp和/b/c/temp - 若需按完整路径过滤(如只忽略
/a/.git但保留/b/.git),得自己封装 walk + 条件跳过,filecmp.dircmp不支持
输出 Git 风格 diff 要自己拼?
是的。filecmp.dircmp 不提供 unified diff 格式,report() 输出是纯文本树状结构,可读性差。要类 Git 输出,得自己构造 --- a/file / +++ b/file 行,并补上增删标记。
- 对
d.left_only中每个路径,输出-<path>表示仅源存在 - 对
d.right_only,输出+<path>表示仅目标存在 - 对
d.diff_files中确认内容不同的文件,可用difflib.unified_diff()读取两文件行内容生成 patch(注意编码和换行符)
最常被跳过的一步:没检查 os.path.isfile() 就直接传路径给 filecmp.cmp() —— 如果同名路径一边是文件、一边是目录,会抛 IsADirectoryError 或静默返回 False,导致误报“内容不同”。务必先用 os.path.isfile() 和 os.path.isdir() 排除类型不一致的情况。


















