什么时候需要xargs?


134

xargs命令总是使我感到困惑。是否有一般规则?

考虑下面的两个示例:

$ \ls | grep Cases | less

打印与“案例”匹配的文件,但是将命令更改为touch则需要xargs

$ \ls | grep Cases | touch
touch: missing file operand
Try `touch --help' for more information.

$ \ls | grep Cases | xargs touch

Answers:


143

不同之处在于目标程序接受什么数据。

如果仅使用管道,它将接收STDIN(标准输入流)上的数据,作为原始数据堆,它可以一次通过一行进行排序。但是,某些程序不接受标准输入的命令,他们希望在命令的参数中将其拼写清楚。例如touch,在命令行上将文件名作为参数,如下所示:touch file1.txt

如果你有一个程序,输出文件名的标准输出和要使用它们作为参数touch,你必须使用xargs它读取STDIN流数据每行转换为空格分隔参数的命令。

这两件事是等效的:

# touch file1.txt
# echo file1.txt | xargs touch

xargs除非您确切知道它在做什么以及为什么需要它,否则不要使用。通常情况下,有一种比xargs强制转换更好的方法来完成这项工作。转换过程还充满了潜在的陷阱,例如转义和单词扩展等。


2
该警告对我来说有点难受。两个常用的选项以获得流到命令行(xargs$(...)),xargs的远比命令替换更安全。而且我不记得曾经遇到过带有换行符的合法文件名。命令替换而不是xargs的转义和单词扩展陷阱不是问题吗?
camh 2011年

6
@camh:两者都是潜在的陷阱。在外壳程序中,您必须担心文件名会在空格,制表符和换行符上分开。在xargs中,您只需要担心换行符。在xargs中,如果输出的格式正确,则可以在NUL字符上分割单词/文件名,而不是(xargs -0),这与结合使用非常有用find -print0
肯·布鲁姆

xargs通过与空格分隔ARGS外壳调用程序,或者它实际上构造参数列表内部(如与使用execv/ execp)?
2011年

1
它在内部构造它并使用execvp,因此很安全。另外,-d \n尽管BSD xargs(OSX等)似乎不支持此选项,但是GNU xargs(在Linux和其他一些Linux上使用)使您可以指定换行符作为分隔符。
蓬松的

72

要扩展已经提供的答案,xargs可以做一件很酷的事情,在当今的多核和分布式计算领域中,它变得越来越重要:它可以并行处理作业。

例如:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8

将一次使用三个过程(-P 3)对* .wav => * .flac进行编码。


哇。我应该在一周前用50GiB的WAV做完全相同的事情(使用OGG除外)时就知道这一点。:)
Alois Mahdal

为什么不使用find具有的-exec参数?
叶夫根尼(Evgeny)2012年

3
@Evgeny该-exec参数不会并行处理作业。
amphetamachine

值得一提的是,-0toxargs参数将其视为NULL输入项定界符。find -print0输出以NULL分隔的项目。对于可能包含空格,引号或其他特殊字符的文件名,这是一个好习惯。
Dan Dascalescu

24

当您在stdin上有一个文件路径列表并且想要对它们进行某些操作时,xargs特别有用。例如:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

让我们逐步研究一下:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....

换句话说,我们的输入是我们要执行的路径的列表。

为了找出xargs对这些路径的作​​用,一个不错的技巧是echo在命令前添加,如下所示:

$ git ls-files "*.tex" | xargs -n 1 echo sed -i "s/color/colour/g"
sed -i "s/color/colour/g" tex/ch1/intro.tex
sed -i "s/color/colour/g" tex/ch1/motivation.tex
....

-n 1参数将使xargs将每一行变成其自己的命令。该sed -i "s/color/colour/g"命令将取代所有出现的colorcolour指定文件。

请注意,这仅在路径中没有空格的情况下才有效。如果这样做,则应通过传递-0标志,将空终止路径用作xargs的输入。一个示例用法是:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"

它与我们上面描述的相同,但是如果其中一条路径中有空格,也可以使用。

这适用于将文件名作为输出生成的任何命令,例如findlocate。如果您确实在带有大量文件的git存储库中使用它,则将它与git grep -l而不是一起使用可能会更有效git ls-files,例如:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"

git grep -l "color" "*.tex"命令将给出包含短语“ color”的“ * .tex”文件列表。


1
没错,但如果你已经学会了这个,你应该了解为什么遍历查找的输出是不好的做法?
通配符

6

您的第一个论点很好地说明了差异。

\ls | grep Cases | less使您可以浏览ls和生成的文件名列表grep。它们恰好是文件名,这只是文本而已。

\ls | grep Cases | xargs less使您可以浏览名称由命令第一部分产生的文件。xargs将文件名列表作为输入并在其命令行上输入命令,并在其命令行上运行带有文件名命令。

当使用考虑xargs,请记住,它预计在输入一种奇怪的方式格式化:空格分隔,用\'以及"用于引用(在一个不寻常的方式,因为\没有特殊的引号内)。仅xargs当您的文件名不包含空格或时使用\'"


@Gilles: xargs可以-0, --null选择解决空格问题(很可能是我从您那里了解到的:),所以我假设您是指无选项xarg调用,但是您对引号的引用感到困惑。您是否有链接或相关示例?..(PS | xargs less是一个方便的“绝招” +1 ..感谢..
Peter.O

4

在您的示例中,您根本不需要使用xargs任何东西,因为find它将完全安全地完成您想做的事情。

您要使用的find正是:

find -maxdepth 1 -name '*Cases*' -exec touch {} +

在此示例中,-maxdepth 1意味着仅在当前目录中进行搜索,而不会进入任何子目录;默认情况下,除非使用maxdepth约束查找,否则find将查找所有子目录(通常是您想要的)。的{}是,将获得取代它的位置和文件名+是两个结束命令标志之一,另一个是;。它们之间的区别在于,这;意味着一次在每个文件上执行该命令,而+意味着一次对所有文件执行该命令。但是请注意,您的外壳可能会尝试解释;自身,因此您需要使用\;或对其进行转义';'。是的,find有很多类似的小烦恼,但它的力量足以弥补这一不足。

两者find和一开始xargs都很难学习。为了帮助您学习,请xargs尝试使用-p--interactive选项,该选项将向您显示即将执行的命令,并提示您是否要运行该命令。

同样,find您可以使用-ok代替-exec提示您是否要运行该命令。

但是,有时有时find无法执行所需的所有操作,而这正是其中的xargs来源。该-exec命令将仅接受{}出现的一个实例,因此,如果您遇到错误,find -type f -exec cp {} {}.bak \;则可以这样做:find -type f -print0 | xargs -0 -l1 -IX cp X X.bak

您可以在GNU Findutils手册中了解有关运行命令的更多信息。

另外,我提到可以find安全地执行您想要的操作,因为当您处理文件时,xargs除非使用-0--null选项以及生成以空字符终止的输入项的东西,否则您将遇到空格和其他会引起问题的字符空格。



@Wildcard文件名中带有空格或字符(例如'或)"可能会出现问题,而find可以毫无问题地处理这些情况。
aculich '16

是的我知道。请参阅我对链接问题的回答。我可能应该在上面的评论中将该问题改写为该问题,或者在其前面添加短语“查看问题...”。:d
通配符

1

xargs(沿findsortduuniqperl和其他几个人)接受命令行开关说“STDIN有文件的列表,由NUL(0×00)字节分开”。这使得处理带有空格和其他有趣字符的文件名变得容易。文件名不包含NUL。


2
我认为您的意思是“文件名不能包含空值”。
amphetamachine
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.