文件整理脚本最容易写的部分是移动和删除,最重要的部分却是确认对象。不同目录中同名的文件可能完全不同,文件名不同的内容也可能相同。
与其立即操作,不如先生成一份可检查的清单。它既是预览,也是操作后的对照依据。
第一步只读,不移动
先记录相对路径、文件大小和内容摘要。路径用于定位,大小可快速排除大部分不相同的文件,摘要则适合进一步比较。
from pathlib import Path
import hashlib
import csv
def digest(path):
result = hashlib.sha256()
with path.open("rb") as stream:
for block in iter(lambda: stream.read(1024 * 1024), b""):
result.update(block)
return result.hexdigest()
root = Path("./documents").resolve()
with open("file-inventory.csv", "w", newline="",
encoding="utf-8") as output:
writer = csv.writer(output)
writer.writerow(["path", "bytes", "sha256"])
for path in sorted(root.rglob("*")):
if path.is_file() and not path.is_symlink():
writer.writerow([str(path.relative_to(root)),
path.stat().st_size, digest(path)])示例将输出写在被扫描目录之外,并跳过符号链接。对于正在持续写入的目录,应先停止写入或使用快照,否则扫描中的大小和内容可能发生变化。
重名冲突要先决定规则
不要让后出现的文件直接覆盖先出现的文件。更可靠的选择,是遇到冲突便停止,或者在清单中列出目标路径,由人检查后再执行。
“相同内容”也不必然等于“应该删除”:两个项目可能各自需要同一个资源。清理前仍应确认引用关系和保留位置。
使用临时归档代替立即删除
确认要清理的文件,可以先移入单独的待清理目录,并记录原始位置。保留一段时间,在相关程序仍能正常运行后,再决定是否永久删除。
同一磁盘上的另一个文件夹方便撤销,但不是独立备份。重要文件还应有另一份可验证的副本。
操作后按清单核对
- 应保留的文件是否仍然存在?
- 移动后的摘要是否与清单一致?
- 发生冲突或读取失败的文件是否被单独列出?
- 能否根据记录恢复原目录结构?
把预览、执行、核对拆开,通常比编写一个“一键整理”按钮更值得。