find 如何防止无限循环(例如,在查找文件时重命名文件)?

Bin*_*rus 2 linux bash find

请考虑以下命令:

find . -type f -name '*.*' -exec mv '{}' '{}_foo' \;
Run Code Online (Sandbox Code Playgroud)

find在这种情况下如何防止无限循环?

一方面,我相信要知道,发现并没有像贝壳水珠工作要做,即它不获取所有的列表*.jpg文件,存储了列表内部,然后处理该列表中的条目。相反,它从底层 O/S 获取文件以“增量”处理,并在知道后立即处理每个文件(让我们忽略可能发生的一定量的缓冲,因为这与问题无关)。毕竟,据我所知,这是在find包含大量文件的目录中优于 glob的主要优势。

如果这是真的,我想了解 find 如何防止无限循环。在上面的示例中,1.jpg将重命名为1.jpg_foo. 从 StackOverflow 和其他地方的讨论中,我知道重命名可能会导致文件(名称)在目录文件列表中占据不同的位置,因此很可能第二次找到该文件,再次将其重命名(为1.jpg_foo_foo),等等在。

显然,这不会发生。

fro*_*utz 5

在单个目录中,它可能就像在处理之前读取整个文件列表一样简单(并且strace看起来就是这样):

# keep reading entries first
openat(AT_FDCWD, ".", O_RDONLY|O_NOCTTY|O_NONBLOCK|O_NOFOLLOW|O_DIRECTORY) = 4
getdents(4, /* 1024 entries */, 32768)  = 32752
getdents(4, /* 1024 entries */, 32768)  = 32768
getdents(4, /* 426 entries */, 32768)   = 13632
getdents(4, /* 0 entries */, 32768)     = 0
close(4)                                = 0
Run Code Online (Sandbox Code Playgroud)

(为了可读性而删节了输出)

# process stuff later
clone(...
wait4(...
--- SIGCHLD...
clone(...
wait4(...
--- SIGCHLD ...
Run Code Online (Sandbox Code Playgroud)

但是,一般来说,find根本不会阻止任何循环。如果您将文件移动到子目录,则会多次发生这种情况:

mkdir -p sub/sub/sub/sub
find -type f -exec mv {} sub/{}_foo \;
Run Code Online (Sandbox Code Playgroud)

这导致sub/sub/sub/sub/file_foo_foo_foo_foo诸如此类的事情。(-depth在这种情况下可能会有所帮助)。

最好首先避免任何可能的冲突,而不是盲目地依赖find使用一些不存在的魔法。您在编辑之前的问题是一个很好的解决方案,因为它根本不匹配已重命名的文件。

即使在没有严格要求的情况下,最好明确说明文件不能也不应该被处理两次。我们在jpg这里重命名文件而不是foo文件。

此外,即使find在一次调用中会阻止处理文件两次,脚本作为一个整体将重新运行并且 find 将运行第二次的风险始终存在,因此您需要采取任何一种保护措施。