乐于分享
好东西不私藏

我让 AI 清理了 18GB 电脑文件,然后总结出这套「不删错」的安全法则

我让 AI 清理了 18GB 电脑文件,然后总结出这套「不删错」的安全法则

一次原本只想「关几个开机启动项」的任务,最后演变成 18GB 缓存清理、270 项废纸篓盘点、179MB 研究资产抢救。踩了 4 个坑之后,我把它沉淀成一套可复用的 Agent 文件安全工作流。

起点:一个失控的小任务

那天我只想问 AI 一句:「看看哪些开机启动项可以关掉?」

结果它顺藤摸瓜,最后交付的是:

  • 清理本地缓存 18GB(其中约 10.9GB 真正释放)
  • 盘点废纸篓 270 项文件,逐个溯源
  • 抢救出 179MB 差点被误清的研究资产
  • 还原 7 个匿名临时目录的真实归属
  • 踩坑 4 次,破例 1 次

整个过程我最大的体会只有一句:

AI 处理批量文件操作时,「安全」比「高效」重要一万倍。

删错一个文件带来的损失,远大于多花十分钟带来的收益。这个不对称,决定了整套方法论的底层逻辑。


一、先立三条铁律

在让 AI 碰你的文件系统之前,规则必须写在配置文件里(Claude Code 是 CLAUDE.md,其他 Agent 同理),而不是靠临场提醒。

铁律 1:永不rm,只用trash

macOS 用 trash,Linux 用 trash-put。文件进回收站可恢复,rm 一去不回。这是唯一一条没有例外的规则。

铁律 2:永不独自彻底删除

rm -rf、rmdir、shred 全部列为禁区。不是「谨慎使用」,是「不使用」。

铁律 3:sudo 属于人类

系统级路径(/Library/... 之类)需要提权,一律交还给用户在终端手动执行。Agent 不碰。

工程上可以再加一层保险:配置 PreToolUse 钩子拦截 rm 类命令(直接 exit 2 阻断)。但说实话,模型的自觉比钩子更重要——钩子只能拦住你想到的模式,自觉才能覆盖你没想到的。


二、五步工作流

Step 1|只读盘点,不删任何东西

第一轮永远是「看」,不是「动」。

du -sh# 看大小find  -maxdepth N -type d -name ”...”# 定位目标cat# 读小文件溯源归属file# 识别真实类型,防扩展名误导shasum -a 1# 哈希对比,确认是否真重复

关键习惯:任何删除动作之前,必须先跑完这一轮。盘点结果用任务列表追踪,让用户能实时看到进度,而不是面对一个黑箱。

Step 2|三色分类

类别
处理方式
🔴 黑名单(门禁)
绝对不动:Agent 会话记录、各类 AI 工具缓存、模型权重
🟢 白名单(可重建缓存)
可批量 trash:明确可重建、明确无业务价值
🟡 待定(不确定)
必须问用户,绝不擅自判断

黄色区域是整套流程的灵魂。「我不确定,所以先清掉」是最危险的思维模式,正确版本是「我不确定,所以先问」。

Step 3|拆任务 + 干跑

每个删除操作前三件事:

  1. 先 ls <path> 列出实际会被影响的文件
  2. 用 --help / man 确认工具的真实行为边界
  3. 检查目标位置是否冲突(比如 trash 会拒绝已在废纸篓里的文件)

Step 4|小批量执行 + 立刻验证

trash   ... && echo ”---verify---” && ls

每批不超过 10 个。出错能立刻定位,绝不「一把梭」。批量越大,回溯成本越高。

Step 5|汇报 + 落 memory

收尾不只是报个数字:

  • 给出实际释放量的前后对比
  • 标出「没动但值得注意」的项目
  • 把新发现的边界写进记忆文件,下次直接复用

三、这次踩的 4 个坑

坑 1:sudo 在 Agent 里没有 TTY

sudo trash /Library/LaunchAgents/xxx.plist# → sudo: a terminal is required to read the password

Agent 的 Bash 子进程没有分配交互式终端,sudo 弹不出密码输入。

正确绕过:让用户在终端手动执行。

绝对不要:echo '密码' | sudo -S ...。密码会同时进入 shell 历史和对话日志,这是拿安全换便利,血亏。

坑 2:trash 拒绝废纸篓里的文件

trash ~/.Trash/some_file# → ”The file doesn't exist”

这是设计使然——避免「回收站套娃」。

正确绕过:需要抢救的用 mv 搬到安全目录;需要彻底清空的让用户在 Finder 里操作。绝不用rm去处理废纸篓,那等于绕过自己立的规矩。

坑 3:包管理器的「幽灵进程」

发现同一个服务注册了两个 launchd 标签,以为是装重复了。实际一查:

which  && ls -la $(which )# → /opt/homebrew/bin/xxx -> /Applications/Xxx.app/Contents/Resources/xxx

包管理器装的只是个软链接,指向图形版应用本体。brew services stop 只会停掉 brew 注册的那个标签,应用本体照跑不误

教训:处理服务前先确认二进制的真实位置,别被两个名字骗了。

坑 4:废纸篓里的体积 ≠ 已释放的空间

清理完用户问「为什么废纸篓还这么大」,我第一反应是「可能有新文件」——其实那 13GB 就是我刚扔进去的。

trash 不是删除,只是搬家。真正释放需要用户手动清空回收站

所以每次清理收尾必须明说:

这 X GB 现在在废纸篓里,需要你在 Finder 右键 →「立即清空」才能真正释放磁盘空间。


四、三个「先问后做」的高危场景

场景 A:废纸篓里可能藏着孤本

我看到一批同名不同大小的媒体文件,第一判断是「重复品」。实际是某个工具反复触发,每次生成的都是新版本——废纸篓里的反而可能比外面的新。

正确做法:

  1. 对废纸篓内外的同名文件做哈希对比
  2. 路径相同但哈希不同 → 废纸篓里的可能是更新版
  3. 用 mv 先把疑似新版搬到安全位置,再考虑清空

场景 B:临时目录不等于可清目录

看到一批 tmp.XXXXXXXXXX/ 这种系统标准临时命名,最容易想当然。实际逐个 cat 关键文件后发现,它们分属两个完全不同的项目——一个是某桌面端的构建产物,一个是某服务的后端日志目录,都还在活跃使用中。

正确做法:先读内容再判断归属,不要凭目录名下结论。

场景 C:文件名会撒谎

废纸篓里有个文件名看起来像某个测试用例,实际 file 一查是 PDF(%PDF-1.5 文件头),后缀只是 mktemp 生成的随机残留。

正确做法:file  永远先看真实类型。扩展名是给人看的,不是给判断用的。


五、和 AI 协作的 5 个实践

1|把决策权交还给人

凡是「我不确定就清掉」的项,一律转成明确的选择题问用户。这次涉及的关键决策包括:保留哪个版本的重复服务、停掉哪些后台数据库、某类运行时缓存是否清理。这些都不该由 AI 单方面拍板。

2|让进度可见

多步骤任务开始前先建任务列表。这次会话创建了 28+ 个任务,覆盖启动项清理、缓存清理、废纸篓抢救、临时目录溯源四条线。用户随时能看到「现在走到哪一步」,而不是等一个大黑箱吐结果。

3|阶段性给对比数据

清理前
清理后
释放
系统缓存目录
6.4 GB
2.0 GB
4.4 GB
用户缓存目录
7.7 GB
4.4 GB
3.3 GB

数字比形容词有说服力。

4|把经验落成记忆

每次发现新的「门禁边界」立刻写进记忆文件,命名带统一前缀,下次一条命令就能捞出来复用。踩过的坑只值得踩一次。

5|给用户可执行清单,而不是模糊承诺

不要说「我帮你清空废纸篓」(这本来就该用户手动做),要说:

  1. 打开 Finder → 侧栏废纸篓
  1. 右键 →「立即清空」
  1. 预计释放约 13 GB

六、工具速查表(不用记忆,因为有agent,但可以对照)

任务
命令
注意
看目录大小
du -sh <path>-s
 只显示顶层汇总
找最大的几项
du -sh *
 配合 sort -rh 与 head -20
定位空间大头
识别文件类型
file <path>
防扩展名误导
哈希对比
shasum -a 1 <f1> <f2>
SHA-1 快,够用
找近期修改
find <path> -mtime -1
单位是天
安全删除
trash <path>
可恢复
看磁盘占用
df -h
系统级视角
看启动项
launchctl list
macOS launchd
看登录项
osascript -e 'tell application "System Events" to get the name of every login item'
AppleScript
看服务注册
brew services list
只反映注册,不反映实际运行

七、一句话总结

AI 处理批量文件 = 慢一点 + 多确认 + 全程 trash + 全部留痕。

速度从来不是这个场景的目标。让用户敢把文件系统交给你操作,才是。

可以把这篇文章交给你的agent,但谨慎的边界,还得自己守住。