删一万个文件,-exec 起一万个进程,-delete 一个都不起
-exec 和 -delete 都能删文件,但坑天差地别:-exec 每个文件起一个进程,删一万个文件起一万个 rm;-delete 不能删非空目录,遇到子目录直接跳过。还有 -exec 末尾分号和加号的区别、xargs 的空格坑。选错命令,要么慢死,要么删不干净。
同样删一万个 .log 文件,三种写法天差地别:
区别在 \; 和 +:分号是"每个文件起一次命令",一万个文件一万个 rm 进程,光 fork 就几十秒;加号是"攒一批文件一起传给一次命令",像 xargs 一样批量,几个 rm 搞定。而 -delete 干脆不 fork 任何外部命令,find 自己用 unlink 系统调用删,最快也最省。所以能 -delete 就 -delete,要 -exec 一定用 + 不用 \;。
下面三个任务,从备选词里点选 token 拼出正确的 find 命令。拼对了会告诉你为什么对,拼错了会告诉你踩了哪个坑。
-delete 快,但它本质是先 depth-first 遍历再 unlink。问题:如果目录非空,unlink 目录会失败(Linux 不允许删非空目录)。所以 find . -delete 遇到嵌套目录时,如果子目录里还有文件没被删,就会跳过那个目录,留下空壳。
解法是用 -depth(先访问子再访问父)配合,或者干脆用 -exec rm -rf {} + 让 rm 自己递归删。但 rm -rf 又危险——它不问青红皂白递归删,路径拼错就删错。这就是 find 的两难:-delete 安全但删不干净,rm -rf 干净但危险。工程上的稳妥写法是先 find ... -print 看一遍要删什么,确认无误再换 -delete。
很多人爱用 find ... | xargs rm,但文件名带空格就炸——xargs 默认按空格切,"my file.txt" 会被切成 "my" 和 "file.txt" 两个参数,rm 删两个不存在的文件。
解法是 find -print0 | xargs -0 rm:-print0 用 null 分隔文件名,-0 让 xargs 按 null 切,空格、换行都不怕。这是 find+xargs 的"安全模式",生产环境必须这么写。同理 -exec ... + 也是安全的(它内部用 null 分隔),所以"安全 + 批量"的写法是 -exec rm {} + 或 -print0 | xargs -0,两者都行,别用 \; 和裸 xargs。
记住这条口诀:分号慢、加号快、-delete 最快但删不净;xargs 怕空格,-print0 配 -0 才安全。能背下这条,find 的坑你踩过 80% 了。