您有很多答案可以很好地解释如何做到这一点;但为了完整起见,我将重复并添加:
xargs 仅对交互使用有用(当您知道所有文件名都是纯格式 - 没有空格或引号时)或与 -0 选项一起使用时。否则,它会破坏一切。
find是一个非常有用的工具;使用它将文件名通过管道传输到xargs(即使使用-0)相当复杂,因为find 可以根据您的需要使用-exec command {} \; 或-exec command {} + 自行完成:
find /path -name 'pattern' -exec somecommand {} \;
find /path -name 'pattern' -exec somecommand {} +
前者运行somecommand 并在/path 中递归地为每个文件运行一个参数 匹配pattern。
后者一次运行somecommand,在命令行中使用尽可能多的参数,以递归方式在/path 中匹配pattern 的文件。
使用哪一个取决于somecommand。如果它可以采用多个文件名参数(如rm、grep 等),那么后一个选项会更快(因为您运行somecommand 的频率要低得多)。如果somecommand 只接受一个参数,那么您需要前一种解决方案。所以看看somecommand的手册页。
更多关于find:http://mywiki.wooledge.org/UsingFind
在bash 中,for 是一个迭代参数的语句。如果你这样做:
for foo in "$bar"
你正在给 for 一个 参数进行迭代(注意引号!)。如果你这样做:
for foo in $bar
您要求bash 获取bar 的内容并将其撕开,只要有空格、制表符或换行符(从技术上讲,是IFS 中的任何字符)并使用该操作的各个部分作为参数为。 这不是文件名。假设在一堆文件名中存在空格的地方将包含文件名的长字符串撕裂的结果是错误的。正如你刚刚注意到的那样。
答案是:不要使用for,这显然是错误的工具。以上find 命令都假定somecommand 是PATH 中的可执行文件。如果它是 bash 语句,则您将需要此构造(迭代 find 的输出,就像您尝试过的一样,但安全):
while read -r -d ''; do
somebashstatement "$REPLY"
done < <(find /path -name 'pattern' -print0)
这使用while-read 循环读取部分字符串find 输出,直到它到达NULL 字节(这是-print0 用于分隔文件名的字节)。由于NULL 字节不能是文件名的一部分(与空格、制表符和换行符不同),这是一个安全的操作。
如果您不需要 somebashstatement 成为脚本的一部分(例如,它不会通过保留计数器或设置变量等来更改脚本环境),那么您仍然可以使用 find' s -exec 运行您的 bash 语句:
find /path -name 'pattern' -exec bash -c 'somebashstatement "$1"' -- {} \;
find /path -name 'pattern' -exec bash -c 'for file; do somebashstatement "$file"; done' -- {} +
这里,-exec 执行带有三个或更多参数的 bash 命令。
- 要执行的 bash 语句。
-
--。 bash 会把这个放在$0 里,你可以放任何你喜欢的东西,真的。
- 您的文件名或文件名(取决于您分别使用的是
{} \; 还是{} +)。文件名以$1 结尾(当然还有$2、$3……如果有不止一个的话)。
此处第一个find 命令中的bash 语句以文件名作为参数运行somebashstatement。
此处第二个find 命令中的bash 语句运行for(!) 循环,该循环遍历每个位置参数(这就是简化的for 语法-@987654387 @ - 确实)并以文件名作为参数运行somebashstatement。我用-exec {} + 展示的第一个find 语句之间的区别在于,我们只为大量文件名运行一个bash 进程,但对于这些文件名中的每个 仍然运行一个somebashstatement。
上面链接的UsingFind页面也很好地解释了所有这些。