使xargs对每行输入执行一次命令


341

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

http://en.wikipedia.org/wiki/Xargs

查找/ path -type f -print0 | xargs -0 rm

在此示例中,find为xargs的输入提供了很长的文件名列表。然后xargs将此列表拆分为子列表,并为每个子列表调用rm一次。这比此功能等效的版本更有效:

找到/ path -type f -exec rm'{}'\;

我知道查找具有“ exec”标志。我只是引用其他资源中的一个示例性例子。


4
在您提供的示例中,find /path -type f -delete效率会更高:)
tzot 2011年

尽量不要使用xargs ...
Naib 2015年

6
OP,我知道这个问题已经很老了,但是它仍然出现在Google和IMHO上,接受的答案是错误的。请参阅下面我的较长答案。
Tobia'3

请考虑将您的接受更改为@Tobia的答案,这会更好。可接受的答案不能处理名称中的空格,并且不允许xargs命令使用多个参数,这是xargs的主要功能之一。
灰色

Answers:


391

仅当输入中没有空格时,以下内容才有效:

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.

13
对我来说,就像xargs -n 1您给的那张表显示“参数列表过长”一样。
2011年

19
如果MAX-LINES省略,则默认为1,这样xargs -l就足够了。请参阅info xargs
2012年

3
@Wernight:“-n1”不对每个输入行进行1次调用。也许您的输入线太长。演示:echo "foo bar" | xargs -n1 echo。因此,如果您输入“ ls”之类的内容,它将无法很好地处理空格。
gatoatigrado

8
错了 -L 1不会回答原始问题,而-n 1只是在一种可能的解释中回答。请参阅下面的详细答案。
Tobia'3

2
@Tobia:它回答了最初的问题,该问题是关于输入行的。就是-L 1这样。在我看来,OP显然正在努力避免默认的分块行为,并且由于接受了这一点,所以我认为我是对的。您的答案针对的是一个稍有不同的用例,在该用例中您还需要分块行为。
Draemon'3

205

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

简介:   如果要“对给定的每一行输入执行一次命令”,并将整行(不带换行符)作为单个参数传递给命令则这是与UNIX兼容的最佳方法:

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

GNU xargs可能有也可能没有有用的扩展,这些扩展使您无法使用tr,但是它们在OS X和其他UNIX系统上不可用。

现在进行详细说明...


使用xargs时要考虑两个问题:

  1. 如何将输入分成“参数”;和
  2. 一次要传递子命令多少个参数。

为了测试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" 

看起来这是更好的答案。但是,我仍然不太了解-L和-n之间的区别是什么?您能解释一下吗?
奥拉拉

5
@olala -L每输入一行执行一次命令(但行末尾的空格将其连接到下一行,并且该行仍根据空格分为多个参数);while -n每个输入参数执行一次命令。如果您->在输出示例中计算的数量,则这些是脚本./show执行的次数。
Tobia'1

我知道了!没有意识到行尾有空格将其连接到下一行。谢谢!
奥拉拉

4
GNU xargs可能有也可能没有有用的扩展,这些扩展使您无法使用tr它。从xargs --help-d,--delimiter = CHARACTER输入流中的项目由CHARACTER而不是空格分隔;禁用引号和反斜杠处理以及逻辑EOF处理
Piotr Dobrogost

这个答案似乎令人困惑-L-L没有说每行执行脚本多少次,而是说一次要消耗多少行输入数据。
Moberg

22

如果您要为来自的每一行(即结果)运行命令find,那么您需要xargs呢?

尝试:

find 命令路径-type f -exec {} \;

文字{}被文件名替换而文字\;需要find来知道自定义命令到此结束。

编辑:

(在修改问题后澄清您知道 -exec

来自man xargs

-L max-lines每个命令行最多
使用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

17

如何使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将换行符转换为null(\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

1
第二个代码块tr'\ n''\ 0 \ => tr'\ n''\ 0'中存在语法错误,我尝试解决此问题,但“编辑必须至少包含6个字符”(这似乎是愚蠢的git拒绝提交,因为我的更改少于6个字符)
htaccess

1
这是什么意思:“使用的另一个问题-L是,每个xargs命令调用都不允许使用多个参数。”?
Moberg

我已经改善了答案,以删除多余的信息@Moberg。
灰色

11

另一种选择

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文件。


这会为每个不是最优的元素发出一个实用程序调用。
2015年

7
find path -type f | xargs -L1 command 

是你所需要的全部。


4

以下命令将在其中找到所有文件(-f型)/path,然后使用将它们复制cp到当前文件夹。注意,如果-I %cp命令行中指定占位符,则可以在文件名之后放置参数。

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

使用xargs(GNU findutils)4.4.0测试


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.

0

似乎我没有足够的声誉在上述Tobia的答案中添加评论,所以我添加了此“答案”以帮助我们这些想尝试的人xargs在Windows平台上相同方法。

这是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.

0

@Draemon的答案似乎与“ -0”正确,即使文件中有空格。

我在尝试xargs命令时发现“ -0”与“ -L”完美配合。即使空格也被处理(如果输入为null终止)。以下是一个示例:

#touch "file with space"
#touch "file1"
#touch "file2"

以下将拆分null并在列表中的每个参数上执行命令:

 #find . -name 'file*' -print0 | xargs -0 -L1
./file with space
./file1
./file2

因此,-L1如果与“ -0”一起使用,将在每个以null终止的字符上执行参数。要查看差异,请尝试:

 #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”才能正常工作。


-3

在您的示例中,将find的输出传递到xargs的意义在于,find的-exec选项的标准行为是对每个找到的文件执行一次命令。如果您使用的是find,并且想要它的标准行为,那么答案很简单-请勿使用xargs开头。


实际上,从OP的编辑中我可以暗示的是,输入数据与无关find,这就是为什么他们不喜欢该-exec选项的原因。
tzot

-3

在当前或子文件夹的每个build.xml上执行ant任务clean-all。

find . -name 'build.xml' -exec ant -f {} clean-all \;

并非所有人都已ant安装。
灰色
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.