在 AI 开发者空间实现只读重复文件检查器:大小分组、SHA-256 与 16 项测试

举报
yd_225105208 发表于 2026/11/05 14:04:53 2026/11/05
【摘要】 在华为云 AI 开发者空间使用 AI Shell 生成并运行 Python 重复文件检查器:先按大小筛选,再分块计算 SHA-256。合成样例识别出 2 组重复文件,16 项测试通过,并核对只读、符号链接、扫描完整性和哈希比较的实际边界。

整理文件时,文件名只能提供线索:两个文件名字不同,内容可能完全相同;名字相同,也可能是两个版本。今天在华为云 AI 开发者空间的免费体验环境里,我使用 AI Shell 辅助生成并运行了一个 Python 小工具,把这个问题拆成“筛选候选文件、比较内容、输出报告”三个步骤。

本次实践只使用 Python 标准库和合成样例。程序输出 JSON 报告,不包含删除、移动或重命名功能。以下代码与测试结果来自本次容器中的实际运行,文末也记录了实现仍有的限制。

1. 先写清楚验收条件

工作目录是 /root/workspace/duplicate-audit-demo,主要产物包括:

duplicate-audit-demo/
├── dupcheck.py
├── test_dupcheck.py
├── README.md
└── examples/

给 AI Shell 的需求包含几个具体约束:接受一个目录参数;递归查找普通文件;跳过符号链接;同名不同内容不能归入重复组;空文件也要处理;无效目录要向标准错误输出原因,并返回非零退出码。生成完成后,还需要实际执行 unittest 和命令行示例。

这些条件比“帮我写一个查重脚本”更容易验收。例如,程序只输出一个“完成了”,无法说明它是否处理了空目录;一份带有命令、退出码和 JSON 输出的记录,则能直接检查结果。

2. 为什么先按大小分组

内容相同的文件,字节数必然相同。因此第一轮先读取文件大小,用大小作为字典键,把候选文件分组。

groups = {}
for path in files:
    size = os.path.getsize(path)
    groups.setdefault(size, []).append(path)

这段展示的是核心逻辑;实际生成的程序还会捕获 OSError,向标准错误输出警告。

只有一个文件的大小分组可以直接跳过。它在本次扫描结果中没有同大小的比较对象,没有必要读取其全部内容。对于其余分组,再计算 SHA-256。

这个筛选不是内容比较的替代品。两个文件即使大小完全相同,也可能每个字节都不同。测试中特意放入了长度相同、末尾只有一个字节不同的两个文件,用来验证后续哈希比较确实在工作。

3. 分块读取,不把大文件一次放入内存

本次实现使用 64 KiB 的读取块:

import hashlib

CHUNK_SIZE = 64 * 1024

def compute_hash(path):
    h = hashlib.sha256()
    with open(path, "rb") as fh:
        while True:
            chunk = fh.read(CHUNK_SIZE)
            if not chunk:
                break
            h.update(chunk)
    return h.hexdigest()

使用二进制模式,是为了比较实际字节,不引入文本解码、换行转换等因素。分块读取限制了单次读入的内容量;256 KiB 的测试文件大于读取块,可以验证循环确实读到了后续内容。

这里不能把“分块读取”说成“整个程序内存恒定”。文件路径列表、大小分组和哈希分组仍然保存在内存中。文件数量很大时,这些结构也会占用明显空间。本次练习没有做百万文件规模测试,也没有给出吞吐量基准。

4. 实际样例与输出

样例中有 11 个普通文件,另外创建了文件符号链接和目录符号链接。主要场景如下:

样例 用来检查什么 实际结果
copy_a.txt、copy_b.txt、copy_c.txt、dir_x/nested_copy.txt 同内容、不同文件名,含子目录 组成一个 4 文件组
dir_x/data.txt、dir_y/data.txt 文件名相同,内容不同 没有归入同一重复组
empty1.dat、empty2.dat 两个零字节文件 组成一个 2 文件组
unique_alpha.txt 等三个文件 内容各不相同 不出现在重复组中
两个符号链接 不跟随链接扫描 不出现在重复组中

在容器中执行:

cd /root/workspace/duplicate-audit-demo
python3 dupcheck.py examples

实际输出为:

[
  [
    "examples/copy_a.txt",
    "examples/copy_b.txt",
    "examples/copy_c.txt",
    "examples/dir_x/nested_copy.txt"
  ],
  [
    "examples/empty1.dat",
    "examples/empty2.dat"
  ]
]

输出路径取决于传入目录的写法。本例传入相对目录 examples,输出也是相对路径,不能因为内部文档写了“absolute paths”就认为它一定返回绝对路径。

不存在的目录会输出 error: '...' does not exist.,返回码为 1;把普通文件当作扫描目录、直接把目录符号链接作为根目录,也会被拒绝。有效的空目录输出 [],返回码为 0。“没有重复文件”和“执行失败”由此可以区分。

5. 16 项测试验证了什么

本次运行命令为:

python3 -m unittest test_dupcheck.py -v

工具执行日志给出的结果是:

Ran 16 tests in 0.445s

OK

测试覆盖了重复组内容、组数量、JSON 数据结构、每组至少两个文件、唯一文件排除、空文件、空目录、单文件目录、符号链接、无效目录、分块读取以及相同长度不同内容等情况。

另一个测试比较了扫描前后样例文件的修改时间 mtime,验证这次运行没有改变它们。这个测试只能支持对应的结论,不能证明所有元数据都不变:读取文件时,文件系统仍可能根据挂载策略更新访问时间 atime。

测试是在这次 Linux 体验容器中执行的。Windows 的符号链接创建权限与行为不同,直接复制测试文件后运行,需要先核对环境,不能把 Linux 的通过结果当作 Windows 的验证结果。

6. 输出报告之后,还需要哪些判断

检查生成代码时,有几处值得保留在使用说明里。

首先,大小与 SHA-256 相同是重复候选判据,程序没有再做逐字节确认。对于要求更严格的场景,可以在同哈希组内再进行字节比较。本次练习没有实现这一步,也不依据报告自动删除文件。

其次,当前实现遇到部分文件的读取或 stat 失败,会警告并跳过,但最终仍可能返回 0;os.walk 没有设置 onerror 回调时,一些目录访问错误也可能被忽略。因此,返回 0 表示当前程序正常结束,不保证扫描完整。若用于备份盘等实际审计,应增加“不完整扫描”的明确状态,并核对标准错误输出。

再次,这个扫描过程不是文件系统快照。检查符号链接、读取大小、打开文件之间存在时间差。如果目录同时被其他程序修改,结果可能不一致。跳过符号链接也不是对恶意目录的安全隔离措施;本次仅在自己创建的静态合成目录中运行。

最后,硬链接还没有按设备号与 inode 去重。相同内容的多条路径可能已经共享同一份磁盘数据,重复组数量不能直接换算为可释放空间。空文件也是同样的例子:两个空文件会被归为重复,但不意味着删除一个就能节约有意义的数据空间。

这次实践让我更关注 AI 生成代码的验收方式:先要求可运行产物和清晰输出,再用样例与测试检查行为,最后逐句核对文档是否比代码作出了更强的承诺。对于这个小工具,当前能确认的是合成样例中的识别结果与 16 项测试,而不是生产目录上的完整性、安全删除或性能保证。

参考入口:华为云 AI 开发者空间 https://developer.huaweicloud.com/devspace 。代码由 AI Shell 辅助生成,运行结果、实现边界与本文说明经逐项核对整理。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。