为何xargs find搭配mv移动成功却输出No such file or directory报错
问题根本原因
报错本质是通配符提前展开加边查找边移动的竞态问题共同导致的:
- 你在命令中写的
./testdir/*,会在整个管道命令被zsh初始解析时就一次性展开为./testdir/dir1、./testdir/dir2、./testdir/dir3、./testdir/dir4四个固定字面路径,作为后续xargs每次调用find时的固定启动参数,这个参数列表是写死的,不会随着目录被移动动态更新。 - 方法1的管道前后段是并行执行的:find每输出一个匹配的目录路径,后面的xargs mv就会立刻把对应目录移到废纸篓;方法2中find每匹配到一个目录,就会立刻通过
-exec触发mv移动目录。 - 后续启动的find进程(甚至同一个find进程的后续执行流程),按照固定参数列表去访问已经被移走的目录路径时,就会抛出
No such file or directory错误。你看到的错误数量逐次增加,刚好对应已经被移走的目录数量逐次累加的规律。
方法3没有报错的原因非常直接:第一步执行的所有find操作仅做路径匹配和输出,全程没有任何移动操作,四个子目录在整个find查找阶段都始终存在;等所有待移动的路径全部收集完成存入变量后,才会统一执行mv操作,此时所有find进程已经退出,不会再去访问已经被移走的路径,自然不会产生报错。
无报错的修复方案
核心思路是避免让会被移动的子目录成为find的固定启动参数,同时避免查找和移动操作的竞态,不需要重定向错误输出即可消除报错:
方案1:修改find的搜索起点
将find的搜索路径从会被通配符展开的./testdir/*,改为始终存在的./testdir目录,同时添加-mindepth 1 -maxdepth 1参数限定仅匹配一级子目录,这样find的启动参数只有不会被删除的testdir目录,不会出现访问不存在的启动路径的问题。
- 修复后的方法1命令:
stat -s ./testdir/* | sed -E -n 's/.*st_ino=([0-9]+).*/\1/p' | xargs -I abc find ./testdir -mindepth 1 -maxdepth 1 -inum abc | xargs -I {} mv {} ~/.Trash
- 修复后的方法2命令:
stat -s ./testdir/* | sed -E -n 's/.*st_ino=([0-9]+).*/\1/p' | xargs -I abc find ./testdir -mindepth 1 -maxdepth 1 -inum abc -exec mv {} ~/.Trash \;
方案2:先完成全量查找再统一移动(最稳妥)
从根源上消除边找边移的竞态:一次性生成所有inode的匹配规则,让find完成全目录扫描、收集完所有待移动路径后,再统一执行mv操作,全程扫描阶段不做任何文件修改。
# 拼接所有inode的匹配条件,一次性查找后批量移动 find ./testdir -mindepth 1 -maxdepth 1 \( $(stat -s ./testdir/* | sed -E -n 's/.*st_ino=([0-9]+).*/-inum \1 -o/p' | tr '\n' ' ' | sed 's/-o $//') \) -exec mv {} ~/.Trash +
内容的提问来源于stack exchange,提问作者Aeronautix
相关产品推荐
相关产品推荐

