在我看来,此页面上的所有现有答案都是错误的,包括标记为正确的答案。这是因为问题措辞含糊。
总结: 如果您想执行命令“每行输入只执行一次”, 将整行(不带换行符)作为 单参数, 那么这是最好的 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 并使其可执行,它的工作原理如下:
$ ./show one two 'three and four'
-> "one" "two" "three and four"
现在,如果最初的问题真的是关于上面的第 2 点(我认为是这样,在阅读了几次之后)并且应该像这样阅读(更改为粗体):
我怎样才能让 xargs 对每个 argument 的输入只执行一次命令?它的默认行为是将输入分块并尽可能少地执行命令,将多个参数传递给每个实例。 em>
那么答案是-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"
另一方面,如果最初的问题是关于第 1 点的输入拆分,并且应该这样阅读(许多来到这里的人似乎认为是这样,或者混淆了这两个问题):
我怎样才能让 xargs 执行命令 with 恰好 一个参数 给定的每一行输入?它的默认行为是将行围绕空格分块。
那么答案就更微妙了。
有人会认为-L 1 可能会有所帮助,但事实证明它不会改变参数解析。它只对每个输入行执行一次命令,参数与该输入行上的参数一样多:
$ 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,则可以将 tr 替换为 GNU 特定选项 -d:
$ echo $'one \ntwo\nthree and four' | xargs -d\\n -n1 ./show
-> "one "
-> "two"
-> "three and four"