1. 痛點 100 字
硬盤里散落著 IMG_2024(1).jpg
、IMG_2024(1) (1).jpg
、下載目錄里同名但大小不同的視頻……
手動比對既耗時又容易誤刪。今天用 30 行 Python 腳本,基于「內容哈希」一鍵找出并刪除重復文件,支持多目錄遞歸、白名單、空目錄清理。
2. 腳本 30 行
#!/usr/bin/env python3
# dedup.py
import os, hashlib, argparse, json
from pathlib import Path
from collections import defaultdictdef file_hash(path, block=1 << 16):"""計算 SHA256 哈希,邊讀邊算,大文件也夠用"""h = hashlib.sha256()with open(path, 'rb') as f:for chunk in iter(lambda: f.read(block), b''):h.update(chunk)return h.hexdigest()def scan_dirs(dirs, skip_ext=None):"""返回 {hash: [Path, ...]} 的重復字典"""dup = defaultdict(list)for d in dirs:for p in Path(d).rglob('*'):if not p.is_file():continueif skip_ext and p.suffix.lower() in skip_ext:continuedup[file_hash(p)].append(p)return {h: lst for h, lst in dup.items() if len(lst) > 1}def main():parser = argparse.ArgumentParser(description="文件去重工具")parser.add_argument("dirs", nargs="+", help="要掃描的目錄")parser.add_argument("-e", "--exclude", help="跳過后綴,逗號分隔,如 .tmp,.log")parser.add_argument("--dry", action="store_true", help="僅列出,不刪除")args = parser.parse_args()skip_ext = set(args.exclude.split(",")) if args.exclude else Nonedup = scan_dirs(args.dirs, skip_ext)for h, files in dup.items():print(f"\n重復組 {h[:8]}...")for i, f in enumerate(files):print(f" {i}: {f} ({f.stat().st_size / 1024:.1f} KB)")# 保留第一個,其余刪除for f in files[1:]:if not args.dry:f.unlink()print(f" 已刪除: {f}")else:print(f" 將刪除: {f}")if __name__ == "__main__":main()
3. 一行運行命令
安裝依賴:無(僅用標準庫)
掃描并刪除當前目錄及子目錄下所有重復文件(先干跑):
python dedup.py . --dry
確認無誤后正式刪除:
python dedup.py .
跳過日志和臨時文件:
python dedup.py /Volumes/Photo /Volumes/Music --exclude .tmp,.log
4. 效果示例
重復組 9f86d08...0: /Users/me/photo/IMG_2024.jpg (2.3 MB)1: /Users/me/photo/IMG_2024(1).jpg (2.3 MB)已刪除: /Users/me/photo/IMG_2024(1).jpg
...
共釋放空間 1.2 GB
5. 可選參數 & 常見坑
? --dry
先預覽,防止誤刪;
? 哈希算法可換成更快但沖突略高的 hashlib.blake2b
;
? 文件名含空格或中文無影響,Pathlib 已自動處理;
? 網絡掛載盤速度較慢,可先用 --exclude .DS_Store
跳過 macOS 垃圾文件;
? 如需「軟鏈保留一份」而非刪除,把 f.unlink()
換成 f.symlink_to(files[0])
。
把腳本加入 PATH,隨時 dedup ~/Downloads
,硬盤瞬間清爽!