请考虑以下命令:
find . -type f -name '*.*' -exec mv '{}' '{}_foo' \;
find
在这种情况下如何防止无限循环?
一方面,我相信知道 find 确实不是像 shell globs 一样工作,即它不会获取所有*.jpg
文件的列表,而是在内部存储该列表,然后处理列出条目。相反,它从底层操作系统“增量”处理文件,并在知道后立即处理每个文件(让我们忽略可能发生的一定量的缓冲,因为这与问题无关)。毕竟,据我所知,这是find
相对于包含大量文件的目录中的 glob 的主要优点。
如果这是真的,我想了解 find 如何防止无限循环。在上面的示例中,1.jpg
将重命名为1.jpg_foo
.从 StackOverflow 和其他地方的讨论中,我知道重命名可能会导致文件(名称)占据目录文件列表中的不同位置,因此 find 可能会再次遇到该文件,然后再次将其重命名(为1.jpg_foo_foo
),等等在。
显然,这不会发生。
答案1
在单个目录中,它可能就像在处理之前读取整个文件列表一样简单(并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
(为便于阅读而对输出进行了删节)
# process stuff later
clone(...
wait4(...
--- SIGCHLD...
clone(...
wait4(...
--- SIGCHLD ...
但一般来说,find
根本不会阻止任何循环。如果将文件移动到子目录,这种情况会发生多次:
mkdir -p sub/sub/sub/sub
find -type f -exec mv {} sub/{}_foo \;
这会导致sub/sub/sub/sub/file_foo_foo_foo_foo
诸如此类的事情。 (-depth
在这种情况下可能有帮助)。
最好从一开始就避免任何可能的冲突,而不是盲目依赖find
使用一些根本不存在的魔法。您在编辑之前提出的问题是一个很好的解决方案,因为它根本与已重命名的文件完全不匹配。
即使在没有严格要求的情况下,最好明确文件不能也不应该被处理两次。我们jpg
在这里重命名文件而不是foo
文件。
此外,即使find
在一次调用中将阻止两次处理文件,整个脚本始终存在重新运行并且 find 将第二次运行的风险,因此无论哪种方式,您都需要采取适当的保护措施。