让 xargs 对每一行输入执行一次命令
Posted
技术标签:
【中文标题】让 xargs 对每一行输入执行一次命令【英文标题】:Make xargs execute the command once for each line of input 【发布时间】:2010-09-16 23:44:24 【问题描述】:如何让 xargs 对给定的每一行输入只执行一次命令? 它的默认行为是将行分块并执行一次命令,将多行传递给每个实例。
来自http://en.wikipedia.org/wiki/Xargs:
查找 /path -type f -print0 | xargs -0 rm
在此示例中,find 为 xargs 的输入提供一长串文件名。然后 xargs 将此列表拆分为子列表,并为每个子列表调用一次 rm。这比这个功能等效的版本更有效:
find /path -type f -exec rm '' \;
我知道 find 有“exec”标志。我只是从另一个资源中引用一个说明性的例子。
【问题讨论】:
在您提供的示例中,find /path -type f -delete
会更有效:)
尽量不要使用 xargs...
OP,我知道这个问题已经很老了,但它仍然出现在谷歌上,恕我直言,接受的答案是错误的。请参阅下面的更长答案。
请考虑将您的接受切换为@Tobia 的答案,这样会更好。接受的答案不处理名称中的空格,并且不允许 xargs 命令的多个参数,这是 xargs 的主要功能之一。
【参考方案1】:
仅当您的输入中没有空格时,以下内容才有效:
xargs -L 1
xargs --max-lines=1 # synonym for the -L option
从手册页:
-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.
【讨论】:
对我来说,它可以输出为xargs -n 1
,因为您给出的那个显示“参数列表太长”。
如果省略MAX-LINES
,则默认为1,因此xargs -l
就足够了。见info xargs
。
@Wernight:“-n1”不会为每个输入行提供 1 次调用。也许您的输入行太长了。演示:echo "foo bar" | xargs -n1 echo
。因此,如果你输入像“ls”这样的东西,它就不能很好地处理空格。
这是错误的。 -L 1
不回答原始问题,-n 1
仅在一种可能的解释中回答。请参阅下面的长答案。
@Tobia:它回答了最初的问题,该问题非常具体地与输入行有关。这正是-L 1
所做的。对我来说,OP 似乎显然是在试图避免默认的分块行为,并且由于这是被接受的,我认为我是对的。您的答案解决了一个稍微不同的用例,您也希望分块行为。【参考方案2】:
您可以分别使用 --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.
【讨论】:
【参考方案3】:在您的示例中,将 find 的输出通过管道传输到 xargs 的目的是 find 的 -exec 选项的标准行为是为每个找到的文件执行一次命令。如果您正在使用 find,并且想要它的标准行为,那么答案很简单 - 不要使用 xargs 开头。
【讨论】:
实际上,我可以从 OP 的编辑中暗示输入数据与find
无关,这就是为什么他们不喜欢@ 987654322@ 选项。【参考方案4】:
如果您想对来自find
的每一行 行(即结果)运行命令,那么您需要xargs
做什么?
试试:
find
路径 -type f -exec
你的命令 \;
其中文字 被文件名替换,
\;
需要文字 find
才能知道自定义命令在那里结束。
编辑:
(在编辑您的问题后澄清您了解-exec
)
来自man xargs
:
-L 最大线数 每个命令行最多使用 max-lines 个非空白输入行。尾随 空白导致输入行在下一个输入行逻辑上继续。 隐含 -x。
请注意,如果您使用xargs
,以空格结尾的文件名会给您带来麻烦:
$ mkdir /tmp/bax; cd /tmp/bax
$ touch a\ b c\ c
$ find . -type f -print | xargs -L1 wc -l
0 ./c
0 ./c
0 total
0 ./b
wc: ./a: No such file or directory
因此,如果您不关心-exec
选项,则最好使用-print0
和-0
:
$ find . -type f -print0 | xargs -0L1 wc -l
0 ./c
0 ./c
0 ./b
0 ./a
【讨论】:
我发现在命令前面加上 echo 'find 很有趣。 -type f -print0 | xargs -0L1 回显 wc -l'。然后,您可以轻松预览 xargs 将生成的命令,例如查看使用“-0L2”时的差异【参考方案5】:find path -type f | xargs -L1 command
就是你所需要的。
【讨论】:
【参考方案6】:以下命令将找到/path
中的所有文件(-type f),然后使用cp
将它们复制到当前文件夹。请注意使用 if -I %
在cp
命令行中指定占位符,以便可以将参数放在文件名之后。
find /path -type f -print0 | xargs -0 -I % cp % .
使用 xargs (GNU findutils) 4.4.0 测试
【讨论】:
【参考方案7】:在当前或子文件夹的每个 build.xml 上执行 ant 任务 clean-all。
find . -name 'build.xml' -exec ant -f clean-all \;
【讨论】:
不是每个人都安装了ant
。【参考方案8】:
另一种选择...
find /path -type f | while read ln; do echo "processing $ln"; done
【讨论】:
【参考方案9】:这两种方式也有效,并且适用于不使用 find 的其他命令!
xargs -I '' rm ''
xargs -i rm ''
示例用例:
find . -name "*.pyc" | xargs -i rm ''
将删除该目录下的所有pyc文件,即使pyc文件包含空格。
【讨论】:
这会为每个非最佳元素发出一个实用程序调用。【参考方案10】:在我看来,此页面上的所有现有答案都是错误的,包括标记为正确的答案。这是因为问题措辞含糊。
总结: 如果您想执行命令“每行输入只执行一次”, 将整行(不带换行符)作为 单参数, 那么这是最好的 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"
【讨论】:
看起来这是更好的答案。但是,我还是不太明白 -L 和 -n 有什么区别……你能解释一下吗? @olala-L
每个输入行执行一次命令(但行尾的空格将其连接到下一行,并且该行仍然根据空格拆分为参数);而-n
对每个输入参数执行一次命令。如果您在输出示例中计算->
的数量,则这些是脚本./show
执行的次数。
GNU xargs
可能有也可能没有有用的扩展,可以让你取消tr
它有一个非常有用的扩展;来自xargs --help
- -d, --delimiter=CHARACTER 输入流中的项目由 CHARACTER 分隔,而不是由空格分隔;禁用引号和反斜杠处理以及逻辑 EOF 处理
@Tobia:您介意我编辑您的答案以包含xargs -d '\n'
作为适用于“某些”系统的tr '\n' '\0' | xargs -0
的快捷方式吗?
@krlmlr 我加了。【参考方案11】:
如何让 xargs 对给定的每一行输入只执行一次命令?
-L 1
是一个简单的解决方案,但如果任何文件中包含空格,它就不起作用。这是 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
【讨论】:
第二个代码块 tr '\n' '\0\ => tr '\n' '\0' 中存在语法错误,我尝试修复此问题,但“编辑必须位于至少 6 个字符”(这似乎和 git 拒绝提交一样愚蠢,因为我的更改少于 6 个字符) 这是什么意思:“使用-L
的另一个问题是它不允许每个xargs
命令调用有多个参数。” ?
我已经改进了我的答案以删除那些无关的信息@Moberg。【参考方案12】:
我似乎没有足够的声望给Tobia's answer above添加评论,所以我添加这个“答案”来帮助我们这些想要在Windows平台上以同样的方式尝试xargs
的人。
这是一个 Windows 批处理文件,它与 Tobia 的快速编码“显示”脚本执行相同的操作:
@echo off
REM
REM cool trick of using "set" to echo without new line
REM (from: http://www.psteiner.com/2012/05/windows-batch-echo-without-new-line.html)
REM
if "%~1" == "" (
exit /b
)
<nul set /p=Args: "%~1"
shift
:start
if not "%~1" == "" (
<nul set /p=, "%~1"
shift
goto start
)
echo.
【讨论】:
【参考方案13】:@Draemon 的答案似乎与“-0”正确,即使文件中有空格。
我正在尝试 xargs 命令,发现“-0”与“-L”完美配合。甚至空格也被处理(如果输入为空终止)。下面是一个例子:
#touch "file with space"
#touch "file1"
#touch "file2"
以下将拆分空值并对列表中的每个参数执行命令:
#find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2
所以-L1
如果与“-0”一起使用,将在每个以空字符结尾的字符上执行参数。要查看差异,请尝试:
#find . -name 'file*' -print0 | xargs -0 | xargs -L1
./file with space ./file1 ./file2
即使这将执行一次:
#find . -name 'file*' -print0 | xargs -0 | xargs -0 -L1
./file with space ./file1 ./file2
该命令将执行一次,因为“-L”现在不会在空字节上拆分。您需要同时提供“-0”和“-L”才能工作。
【讨论】:
以上是关于让 xargs 对每一行输入执行一次命令的主要内容,如果未能解决你的问题,请参考以下文章
Linux xargs 命令(给命令传递参数的一个过滤器,也是组合多个命令的一个工具)(通常与管道配合使用)