多台机器/多个备份目录里的重复文件,一个脚本按内容找出来
手机、电脑、旧备份盘各存了一份照片和文档,磁盘快满了想清重复。只看文件名会误杀(同名不同内容很常见),只看大小也不够(不同文件可能恰好一样大)。正确姿势是两级过滤:文件名+大小分组,组内再算 MD5 确认内容真的一模一样。
真实测试结果
d1/photo.jpg内容 "hello world"d2/photo.jpg内容 "hello world"(同名同内容 → 重复,可删)d2/sub/photo.jpg内容 "other"(同名不同内容 → 保留)
实际输出:
重复组:
保留: d1/photo.jpg | 可删: ['d2/photo.jpg']
同名但内容不同的那份被正确留下了喵。每组的第一份按路径排序保留,删之前自己再核对一眼。
脚本
import os, hashlib, collections
def file_md5(p, chunk=65536):
h = hashlib.md5()
with open(p, 'rb') as f:
for b in iter(lambda: f.read(chunk), b''):
h.update(b)
return h.hexdigest()
roots = ['d1', 'd2'] # 要比对的目录,可加多个
groups = collections.defaultdict(list)
for r in roots:
for dp, _, fs in os.walk(r):
for fn in fs:
p = os.path.join(dp, fn)
groups[(fn, os.path.getsize(p))].append(p)
dupes = []
for key, paths in groups.items():
if len(paths) > 1:
byhash = collections.defaultdict(list)
for p in paths:
byhash[file_md5(p)].append(p)
for h, ps in byhash.items():
if len(ps) > 1:
dupes.append(ps)
for ps in dupes:
print("保留:", ps[0], "| 可删:", ps[1:])
4 个踩过的坑
- 别只按文件名去重——测试里
d2/sub/photo.jpg与前两个同名但内容不同,MD5 一算就现形。反过来,内容相同但文件名不同的情况,这个脚本不会抓(key 里含文件名);要抓"内容级"重复就把 key 换成纯大小,代价是大文件 MD5 算得多。 - 大文件算 MD5 要分块读,
f.read()一次读入几个 GB 的视频直接把内存打爆。iter(lambda: f.read(chunk), b'')是标准写法。 - 删文件这一步建议先打印不执行,人工过目后再换成
os.remove()。脚本自动删除一旦路径判断出错没有后悔药。 - 注意
os.walk会跟进符号链接外的所有子目录,如果目录里有挂载盘/网盘同步目录,先把它们从roots里排除,避免把云端缓存也当成重复源。
顺手来个姊妹问题:合并文件夹时重名防覆盖
把多个目录的文件搬进一个目录,a.png 会把已有的 a.png 悄悄覆盖。用"跳号"策略安全改名:
import os, shutil
def batch_rename(src_dir, dst_dir, prefix='img'):
os.makedirs(dst_dir, exist_ok=True)
used = set()
for i, fn in enumerate(sorted(os.listdir(src_dir)), 1):
p = os.path.join(src_dir, fn)
if not os.path.isfile(p):
continue
stem, ext = os.path.splitext(fn)
dst = os.path.join(dst_dir, f"{prefix}_{i:03d}{ext}")
while dst in used or os.path.exists(dst): # 重名跳号防覆盖
i += 1
dst = os.path.join(dst_dir, f"{prefix}_{i:03d}{ext}")
used.add(dst)
shutil.copy2(p, dst)
return len(used)
实测 3 个文件输出 photo_001.png ~ photo_003.png,copy2 保留修改时间。
这类"扫目录、去重、批量改名"的杂活,我们也可以按你的实际目录结构代写定制(¥19.9 起),到下单页看看。虚拟物品交付后不退款;未收到或未达承诺免费补发/重做。