您有很多答案可以很好地解释如何做到这一点;但为了完整起见,我将重复并添加:
xargs
仅对交互式使用有用(当您知道所有文件名都是普通的 - 没有空格或引号时)或与-0
选项一起使用时。否则,它会破坏一切。
find
是一个非常有用的工具;put 使用它来将文件名通过管道传输到xargs
(即使使用-0
)相当复杂,因为find
它可以自己完成,-exec command {} \;
或者-exec command {} +
取决于你想要的:
find /path -name 'pattern' -exec somecommand {} \;
find /path -name 'pattern' -exec somecommand {} +
前者在match 中递归地为每个文件somecommand
运行一个参数。/path
pattern
后者在该 match中以递归方式一次在命令行上somecommand
使用尽可能多的参数运行文件。/path
pattern
使用哪一个取决于somecommand
. 如果它可以采用多个文件名参数(如rm
,grep
等),那么后一个选项会更快(因为你运行somecommand
的频率要低得多)。如果somecommand
只接受一个参数,那么您需要前一种解决方案。所以看看somecommand
's man page。
更多关于find
:http://mywiki.wooledge.org/UsingFind
In bash
,for
是一个迭代参数的语句。如果你做这样的事情:
for foo in "$bar"
你给了for
一个参数来迭代(注意引号!)。如果你做这样的事情:
for foo in $bar
您要求在有空格、制表符或换行符的地方(从技术上讲,任何字符在 中)都bash
将其内容bar
撕开,IFS
并将该操作的各个部分用作 for 的参数。 那不是文件名。假设在一堆文件名中存在空格的地方将包含文件名的长字符串撕裂的结果是错误的。正如你刚刚注意到的那样。
答案是:不要使用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
此处第一个命令中的bash
语句以文件名作为参数运行。find
somebashstatement
这里第二个命令中的bash
语句运行一个( ! ) 循环,该循环遍历每个位置参数(这就是简化的语法 - - 所做的)并以文件名作为参数运行 a 。我展示的第一个语句之间的区别在于,我们只为大量文件名运行一个进程,但仍然为每个文件名运行一个进程。find
for
for
for foo; do
somebashstatement
find
-exec {} +
bash
somebashstatement
UsingFind
所有这些在上面链接的页面中也得到了很好的解释。