在我看来,此页面上的所有现有答案都是错误的,包括标记为正确的答案。这是因为问题措辞含糊不清。
总结: 如果你想“每行输入只执行一次”命令,将整行(不带换行符)作为单个参数传递给命令,那么这是最好的 UNIX 兼容方式:
... | tr '\n' '\0' | xargs -0 -n1 ...
如果您使用 GNUxargs
并且不需要与所有其他 UNIX(FreeBSD、Mac OS X 等)兼容,那么您可以使用 GNU-specific 选项-d
:
... | xargs -d\\n -n1 ...
现在进行冗长的解释……</p>
使用 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 对给定的每个输入参数只执行一次命令?它的默认行为是将输入分成参数并尽可能少地执行命令,将多个参数传递给每个实例。
那么答案是-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为给定的每一行输入只使用一个参数来执行命令?它的默认行为是将空格周围的行分块。
那么答案就更微妙了。
有人会认为这-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 字节。
然后,只需借助以下方法将换行符转换为 NUL 即可tr
:
$ 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"