Bash 对找到的文件执行命令

示例

有时我们需要对许多文件运行命令。可以使用来完成xargs。

find . -type d -print | xargs -r chmod 770

上面的命令将递归地找到-type d相对于.(当前工作目录)的所有目录(),并chmod 770在它们上执行。该-r选项指定如果未找到任何文件xargs则不运行。chmodfind

如果文件名或目录中包含空格,则此命令可能会阻塞;一个解决方案是使用以下

find . -type d -print0 | xargs -r -0 chmod 770

在上面的示例中,-print0和-0标志指定文件名将使用null字节分隔,并允许在文件名中使用特殊字符(例如空格)。这是一个GNU扩展,不得在其他版本的find和xargs。


执行此操作的首选方法是跳过xargs命令,然后find调用子流程本身:

find . -type d -exec chmod 770 {} \;

在这里,{}是一个占位符,指示您此时要使用文件名。find将分别chmod在每个文件上执行。

您也可以通过所有的文件名到单一的呼叫chmod,通过使用

find . -type d -exec chmod 770 {} +

这也是上述xargs片段的行为。(要单独调用每个文件,可以使用xargs -n1)。


第三种选择是让bash在文件名find输出列表上循环:

find . -type d | while read -r d; do chmod 770 "$d"; done

从语法上讲,这是最笨拙的,但是当您要在每个找到的文件上运行多个命令时,这很方便。但是,面对带有奇数名称的文件名,这是不安全的。

find . -type f | while read -r d; do mv "$d" "${d// /_}"; done

这将用下划线替换文件名中的所有空格。(如果前导目录名称中有空格,则此示例也将不起作用。)

上面的问题是while read -r期望每行有一个条目,但是文件名可以包含换行符(并且还会read -r丢失任何尾随的空格)。您可以通过以下方法解决此问题:

find . -type d -exec bash -c 'for f; do mv "$f" "${f// /_}"; done' _ {} +

这样,-exec接收方将以完全正确且可移植的形式接收文件名;在bash -c接收它们的数量的参数,这将在中找到$@,正确地引用等。(脚本需要正确处理,当然这些名字,;它包含一个文件名需要每一个变量是在双引号)

神秘_是必要的,因为to的第一个参数bash -c 'script'用于填充$0。