在我看来,此页面上所有现有的答案都是错误的,包括标记为正确的答案。这是因为这个问题的措词含糊不清。
简介: 如果要“对给定的每一行输入执行一次命令”,并将整行(不带换行符)作为单个参数传递给命令,则这是与UNIX兼容的最佳方法:
... | tr '\n' '\0' | xargs -0 -n1 ...
GNU xargs
可能有也可能没有有用的扩展,这些扩展使您无法使用tr
,但是它们在OS X和其他UNIX系统上不可用。
现在进行详细说明...
使用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"
find /path -type f -delete
效率会更高:)