理解UNIX命令xargs

40

我对这个问题感到相当困惑。需要一些解释。

例子1:

pgrep string | xargs ps

例子 2:

find . | xargs grep whatever

从示例1中,我得出的结论是这样的:

搜索运行进程名称中包含特定“字符串”的进程并返回所有匹配项的进程ID,然后将这些进程ID附加到'xargs ps'命令中以获取与以下命令相同的输出:

ps <processid>
请问在这种情况下xargs具体是做什么的呢?
从示例2中,我了解到它是这样的:
它会在当前工作目录递归查找一些“字符串”,那么这里'xargs'是如何起作用的呢?
我认为'xargs'会将标准输入中的数据重复追加到xargs给出的“参数”中(通常本身就是一个UNIX命令)。
根据xargs()手册:
xargs从标准输入中读取项目,由空格分隔(可以使用双引号或单引号或反斜杠进行保护),并执行命令(默认为/bin/echo)一次或多次,任何初始参数后跟从标准输入读取的项目。 忽略标准输入中的空行。

1
xargs的作用类似于“命令替换”(至少在Bash中是这样)。它将多行结果(垂直)转换为一个标记参数列表(水平)。 (请注意,在通过xargs之前,您可能需要使用sed等工具对结果进行一些过滤)。此外,xargs处理了在Linux内核2.6.23之前可能发生的“太多参数”错误(请参见维基百科)。这里有另一个有用的主题:何时需要xargs - Stphane
5个回答

60

xargs 通常的使用方法如下:

prog | xargs 命令行工具

其中 prog 预计输出一个或多个由换行符或空格隔开的结果。关键在于,xargs 并不一定为每个结果单独调用 命令行工具,而是将结果拆分成子列表,并为每个子列表调用 命令行工具。如果您想强制 xargs 为每个结果都调用 命令行工具,则需要使用 xargs -L1 命令。

请注意,xargs 保证发送给 命令行工具 的子列表长度小于 ARG_MAX(如果您感兴趣,可以使用 getconf ARG_MAX 命令获得当前的 ARG_MAX 值)。这就是它避免了那些可怕的 "Argument list too long" 错误。


1
嗯,这是我能够理解的事情,但现在它变得更加混乱了。我知道 xargs 存在的基本原因,但当我看到它被用于不止一个目的时(以不同的方式),它变得很复杂。 - halluc1nati0n
7
考虑以下命令“find /etc -type d -depth 1 | xargs echo”,该命令打印出/ etc文件夹中的所有目录(但不包括其子目录)。由于echo可以接收多个参数,结果是一行长字符串“/ etc /dir1 / etc /dir2 ...”。如果您调用“find /etc -type d -depth 1 | xargs -L1 echo”,则对于每个结果都会调用echo,因此从/ etc获取的每个目录都会单独打印在一行上。 - Lars Tackmann
我如何将命令或选项传递给实用程序?比如说,我想运行uglifyjs并为输入进入xargs指定一个输出文件夹?http://stackoverflow.com/questions/43149786/how-to-process-files-in-nested-directories - Costa Michailidis

17

xargs的一个很好的示例是尝试使用find获取目录中每个文件的排序校验和。

find . | cksum  | sort

返回的仅是一个校验和,而不清楚这个校验和是用于哪里。这并非我们所需要的。管道将find的标准输出发送到cksum的标准输入中。Cksum实际上想要的是一系列命令行参数,例如:

cksum file001.blah file002.blah  file003.blah

这个命令将会汇报三行内容,每行一个文件的期望校验和。Xargs可以实现魔法般的功能——将前一个程序的标准输出转换为临时的、隐藏的命令行,并将其提供给下一个程序。适用的命令行是:

find . | xargs cksum | sort

注意在xargs和cksum之间没有管道符号。


2
顺便说一下,这是我用于查找两个或多个目录中重复文件的主要方法,即使它们的名称不同。 - DarenW
感谢您的见解,将 verbose 添加到 xargs 中可以让命令生效。查找 . | xargs --verbose cksum | sort - kumar
find . | grep / | xargs cksum | sort may be used to avoid the unwanted output cksum: .: Is a directory - Jarvis
1
@Jarvis 更好的做法是将查找命令改为:*find . \! -type d*。 - Pryftan

8
$ echo 'line1
> line2
> line3
> ...
> lineN ' | xargs cmd1 -a -b

会导致:
$ cmd1 -a -b line1 line2 line3 ... lineN
xargs会将cmd1 ...分成多个cmd1执行,如果行数过多。 xargs可用于许多与传递stdin行作为位置参数相关的任务。请查看xargs(1)中的大写-P选项,以并行运行多个命令实例。

4
#!/bin/sh
#script to echo out the arguments 1 at a time!
for a in $*
do
    echo $a
done

该命令

$sh myscript 1 2 3 4 5

将产生

1
2
3
4
5

但是
$sh myscript 1 2 3 4 5 6 7 8 9 10 11

由于参数数量超过了最大值(实际上我不确定最大值是多少,但在这个例子中我们假设它是10),所以此方法将不起作用!

为了解决这个问题,我们可以使用:

#!/bin/sh
#script to echo out the arguments 1 at a time!
for a in $*
do
    echo $a | xargs echo
done

我们可以这样运行它。
 $sh myscript "1 2 3 4 5" "6 7 8 9 10 11"

由于只有两个参数,因此可以获得正确的结果


2
我不知道最大值是多少,但绝对不是10。并且你可以使用$@代替$*。这不是如何使用xargs的很好的例子。 - ghostdog74
@ghostdog74 关于那个问题,你可以尝试使用 getconf ARG_MAX 命令。当然,这是 C 程序员所熟知的内容,但无论如何:它表示的是“用于 exec() 的参数和环境变量所占用的字节数”。在 Linux 上,它被定义在 /usr/include/linux/limits.h 文件中。对于你提到的其他问题,也是正确的。 - Pryftan
至于回答者Paul:我认为你的答案可以稍微改进一下,加上“$”和“sh”之间的空格。甚至可以删除“$”,因为它不是命令的一部分。乍一看(在我的情况下是视力不佳+疲惫不堪),它看起来像是shell变量。此外,您应该在脚本中引用bash变量。正如@ghostdog74所指出的那样,您应该将其更改为“$@”。干杯。 - Pryftan

2

xargs通常用于将参数分组,以避免在向命令传递大量参数时出现“参数过多”错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接