什么时候需要 xargs?

Zai*_*aid 140 command-line xargs

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

考虑下面的两个例子:

$ \ls | grep Cases | less
Run Code Online (Sandbox Code Playgroud)

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

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

$ \ls | grep Cases | xargs touch
Run Code Online (Sandbox Code Playgroud)

Cal*_*leb 150

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

如果您只使用管道,它会在 STDIN(标准输入流)上接收数据作为原始数据堆,它可以一次对一行进行排序。然而,有些程序不接受标准输入中的命令,他们希望在命令的参数中将其拼写出来。例如touch需要一个文件名作为像这样在命令行上的参数:touch file1.txt

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

这两件事是等价的:

# touch file1.txt
# echo file1.txt | xargs touch
Run Code Online (Sandbox Code Playgroud)

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

  • @camh:它们都是潜在的陷阱。在 shell 中,您必须担心文件名在空格、制表符和换行符上被拆分。在 xargs 中,您只需要担心换行符。在 xargs 中,如果您的输出格式正确,您可以在 NUL 字符上拆分单词/文件名(`xargs -0`),这与 `find -print0` 结合使用非常有用。 (6认同)
  • 警告对我来说有点牵强。在将流输入命令行的两个常用选项(`xargs` 和 `$(...)`)中,xargs 比命令替换安全得多。而且我不记得曾经遇到过带有换行符的合法文件名。不是命令替换的转义和单词扩展陷阱问题,而不是 xargs 吗? (2认同)

amp*_*ine 73

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

例如:

$ find . -type f -name '*.wav' -print0 |xargs -0 -P 3 -n 1 flac -V8
Run Code Online (Sandbox Code Playgroud)

将编码 *.wav => *.flac,同时使用三个进程 ( -P 3)。

  • @Evgeny `-exec` 参数不会并行处理作业。 (5认同)

Sve*_*ier 25

当您在 stdin 上有文件路径列表并想对它们做一些事情时,xargs 特别有用。例如:

$ git ls-files "*.tex" | xargs -n 1 sed -i "s/color/colour/g"
Run Code Online (Sandbox Code Playgroud)

让我们一步一步地检查一下:

$ git ls-files "*.tex"
tex/ch1/intro.tex
tex/ch1/motivation.tex
....
Run Code Online (Sandbox Code Playgroud)

换句话说,我们的输入是我们想要做某事的路径列表。

要找出 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
....
Run Code Online (Sandbox Code Playgroud)

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

请注意,这仅在您的路径中没有任何空格时才有效。如果这样做,您应该通过传递-0标志来使用空终止路径作为 xargs 的输入。一个示例用法是:

$ git ls-files -z "*.tex" | xargs -0 -n 1 sed -i "s/color/colour/g"
Run Code Online (Sandbox Code Playgroud)

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

这适用于任何生成文件名作为输出的命令,例如findlocate。如果您碰巧在包含大量文件的 git 存储库中使用它,使用它git grep -l代替可能更有效git ls-files,如下所示:

$ git grep -l "color" "*.tex" | xargs -n 1 sed -i "s/color/colour/g"
Run Code Online (Sandbox Code Playgroud)

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


Gil*_*il' 8

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

\ls | grep Cases | less可让您浏览由ls和生成的文件名列表grep。它们碰巧是文件名并不重要,它们只是一些文本。

\ls | grep Cases | xargs less允许您浏览名称由命令的第一部分生成的文件。xargs将文件名列表作为输入和命令行上的命令,并在命令行上使用文件名运行命令。

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


acu*_*ich 5

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

正是您想要使用的find是:

find -maxdepth 1 -name '*Cases*' -exec touch {} +
Run Code Online (Sandbox Code Playgroud)

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

两者find和 一开始xargs都很难学。为了帮助您学习,请xargs尝试使用-por--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除非你使用-0or--null选项以及生成以空字符结尾的输入项的东西的空白。