如何使xargs对给定的每一行输入执行一次命令? 它的默认行为是将行分块并执行一次命令,将多行传递给每个实例。

从http://en.wikipedia.org/wiki/Xargs: 查找/path -type f -print0 | xargs -0 rm 在本例中,find为xargs的输入提供一长串文件名。然后Xargs将这个列表拆分为子列表,并对每个子列表调用rm一次。这比功能等效的版本更有效: 查找/path -type f -exec rm '{}' \;

我知道那个发现有"执行"的标志。我只是从另一种资源中引用了一个说明性的例子。


当前回答

在我看来,这页上现有的所有答案都是错误的,包括被标记为正确的那个。这是因为这个问题措辞含糊。

总结:如果你想“对每一行输入只执行一次”,将整行(不带换行符)作为一个参数传递给命令,那么这是最好的unix兼容方式:

... | tr '\n' '\0' | xargs -0 -n1 ...

如果你正在使用GNU xargs,并且不需要与所有其他UNIX兼容(FreeBSD, Mac OS X等),那么你可以使用特定于GNU的选项-d:

... | xargs -d\\n -n1 ...

现在来解释一下…


在使用xargs时,有两个问题需要考虑:

它如何将输入分成“参数”;而且 一次传递子命令多少个参数。

为了测试xargs的行为,我们需要一个实用程序来显示它被执行了多少次以及有多少个参数。我不知道是否有一个标准的实用程序来做到这一点,但我们可以很容易地在bash中编码:

#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo

假设你将它保存在当前目录中,并使其可执行,下面是它的工作原理:

$ ./show one two 'three and four'
-> "one" "two" "three and four" 

如果最初的问题是关于第二点的。上面(我认为是这样,读了几遍之后),它是这样读的(粗体的变化):

我如何能使xargs执行命令准确地为输入的每个参数给出一次?它的默认行为是将输入分成参数块,并尽可能少地执行命令,将多个参数传递给每个实例。

那么答案是-n 1。

让我们比较一下xargs的默认行为,它围绕空白分隔输入,并尽可能少地调用命令:

$ echo one two 'three and four' | xargs ./show 
-> "one" "two" "three" "and" "four" 

当-n为1时

$ echo one two 'three and four' | xargs -n 1 ./show 
-> "one" 
-> "two" 
-> "three" 
-> "and" 
-> "four" 

另一方面,如果最初的问题是关于第一点的。输入分割,它是这样读的(许多人似乎认为是这样的情况,或者混淆了这两个问题):

我怎么能让xargs执行命令,每行输入给出一个参数?它的默认行为是将空格周围的行分成块。

那么答案就更加微妙了。

有人会认为- l1可能会有帮助,但事实证明它不会改变参数解析。对于每个输入行,它只执行一次命令,并带有与该输入行相同的参数:

$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" 
-> "two" 
-> "three" "and" "four" 

不仅如此,如果一行以空格结束,它会被追加到下一行:

$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" "two" 
-> "three" "and" "four" 

显然,-L不是要改变xargs将输入分成参数的方式。

以跨平台方式(不包括GNU扩展)执行此操作的唯一参数是-0,它围绕NUL字节分割输入。

然后,只需在tr的帮助下将换行符转换为NUL即可:

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show 
-> "one " "two" "three and four" 

现在参数解析看起来很正常,包括后面的空格。

最后,如果你将这种技术与-n 1结合起来,你将在每个输入行中得到一个命令执行,无论你有什么输入,这可能是另一种看待原始问题的方式(可能是最直观的,考虑到标题):

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

如上所述,如果你正在使用GNU xargs,你可以用特定于GNU的选项-d替换tr:

$ echo $'one \ntwo\nthree and four' | xargs -d\\n -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

其他回答

您可以分别使用——max-lines或——max-args标志限制行数或参数(如果每个参数之间有空格)。

-L max-lines Use at most max-lines nonblank input lines per command line. Trailing blanks cause an input line to be logically continued on the next input line. Implies -x. --max-lines[=max-lines], -l[max-lines] Synonym for the -L option. Unlike -L, the max-lines argument is optional. If max-args is not specified, it defaults to one. The -l option is deprecated since the POSIX standard specifies -L instead. --max-args=max-args, -n max-args Use at most max-args arguments per command line. Fewer than max-args arguments will be used if the size (see the -s option) is exceeded, unless the -x option is given, in which case xargs will exit.

find path -type f | xargs -L1 command 

这就是你所需要的。

如何使xargs对给定的每一行输入执行一次命令?

- l1是一个简单的解决方案,但如果任何文件中包含空格,它就不起作用。这是find的-print0参数的一个关键函数,用'\0'字符而不是空格分隔参数。这里有一个例子:

echo "file with space.txt" | xargs -L 1 ls
ls: file: No such file or directory
ls: with: No such file or directory
ls: space.txt: No such file or directory

更好的解决方案是使用tr将换行符转换为空(\0)字符,然后使用xargs -0参数。这里有一个例子:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 ls
file with space.txt

如果你需要限制调用的次数,你可以使用-n 1参数为每个输入对程序进行一次调用:

echo "file with space.txt" | tr '\n' '\0' | xargs -0 -n 1 ls

这还允许您在将换行符转换为空之前过滤find的输出。

find . -name \*.xml | grep -v /target/ | tr '\n' '\0' | xargs -0 tar -cf xml.tar

在我看来,这页上现有的所有答案都是错误的,包括被标记为正确的那个。这是因为这个问题措辞含糊。

总结:如果你想“对每一行输入只执行一次”,将整行(不带换行符)作为一个参数传递给命令,那么这是最好的unix兼容方式:

... | tr '\n' '\0' | xargs -0 -n1 ...

如果你正在使用GNU xargs,并且不需要与所有其他UNIX兼容(FreeBSD, Mac OS X等),那么你可以使用特定于GNU的选项-d:

... | xargs -d\\n -n1 ...

现在来解释一下…


在使用xargs时,有两个问题需要考虑:

它如何将输入分成“参数”;而且 一次传递子命令多少个参数。

为了测试xargs的行为,我们需要一个实用程序来显示它被执行了多少次以及有多少个参数。我不知道是否有一个标准的实用程序来做到这一点,但我们可以很容易地在bash中编码:

#!/bin/bash
echo -n "-> "; for a in "$@"; do echo -n "\"$a\" "; done; echo

假设你将它保存在当前目录中,并使其可执行,下面是它的工作原理:

$ ./show one two 'three and four'
-> "one" "two" "three and four" 

如果最初的问题是关于第二点的。上面(我认为是这样,读了几遍之后),它是这样读的(粗体的变化):

我如何能使xargs执行命令准确地为输入的每个参数给出一次?它的默认行为是将输入分成参数块,并尽可能少地执行命令,将多个参数传递给每个实例。

那么答案是-n 1。

让我们比较一下xargs的默认行为,它围绕空白分隔输入,并尽可能少地调用命令:

$ echo one two 'three and four' | xargs ./show 
-> "one" "two" "three" "and" "four" 

当-n为1时

$ echo one two 'three and four' | xargs -n 1 ./show 
-> "one" 
-> "two" 
-> "three" 
-> "and" 
-> "four" 

另一方面,如果最初的问题是关于第一点的。输入分割,它是这样读的(许多人似乎认为是这样的情况,或者混淆了这两个问题):

我怎么能让xargs执行命令,每行输入给出一个参数?它的默认行为是将空格周围的行分成块。

那么答案就更加微妙了。

有人会认为- l1可能会有帮助,但事实证明它不会改变参数解析。对于每个输入行,它只执行一次命令,并带有与该输入行相同的参数:

$ echo $'one\ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" 
-> "two" 
-> "three" "and" "four" 

不仅如此,如果一行以空格结束,它会被追加到下一行:

$ echo $'one \ntwo\nthree and four' | xargs -L 1 ./show 
-> "one" "two" 
-> "three" "and" "four" 

显然,-L不是要改变xargs将输入分成参数的方式。

以跨平台方式(不包括GNU扩展)执行此操作的唯一参数是-0,它围绕NUL字节分割输入。

然后,只需在tr的帮助下将换行符转换为NUL即可:

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 ./show 
-> "one " "two" "three and four" 

现在参数解析看起来很正常,包括后面的空格。

最后,如果你将这种技术与-n 1结合起来,你将在每个输入行中得到一个命令执行,无论你有什么输入,这可能是另一种看待原始问题的方式(可能是最直观的,考虑到标题):

$ echo $'one \ntwo\nthree and four' | tr '\n' '\0' | xargs -0 -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

如上所述,如果你正在使用GNU xargs,你可以用特定于GNU的选项-d替换tr:

$ echo $'one \ntwo\nthree and four' | xargs -d\\n -n1 ./show 
-> "one " 
-> "two" 
-> "three and four" 

下面的命令将找到/path中的所有文件(-type f),然后使用cp将它们复制到当前文件夹。注意,使用if -I %在cp命令行中指定一个占位符,以便参数可以放在文件名之后。

find /path -type f -print0 | xargs -0 -I % cp %。

用xargs (GNU findutils) 4.4.0测试