为什么需要 xargs?

为什么需要 xargs?

假设我想删除目录中除名为“notes.txt”的文件之外的所有文件。我会使用管道执行此操作。ls | grep -v "notes.txt" | xargs rm如果第二个管道的输出是 rm 应该使用的输入,为什么我需要 xargs?

为了便于比较,管道echo "#include <knowledge.h>" | cat > foo.c将回显的文本插入到文件中,而无需使用 xargs。这两个管道有什么区别?

答案1

您混淆了两种截然不同的输入:STDIN 和参数。参数是命令启动时提供给命令的字符串列表,通常是在命令名称后指定它们(例如echo these are some argumentsrm file1 file2)。另一方面,STDIN 是命令启动后可以(可选)读取的字节流(有时是文本,有时不是)。以下是一些示例(请注意 可以cat接受参数或 STDIN,但对它们的作用不同):

echo file1 file2 | cat    # Prints "file1 file2", since that's the stream of
                          # bytes that echo passed to cat's STDIN
cat file1 file2    # Prints the CONTENTS of file1 and file2
echo file1 file2 | rm    # Prints an error message, since rm expects arguments
                         # and doesn't read from STDIN

xargs可以被认为是将 STDIN 样式的输入转换为参数:

echo file1 file2 | cat    # Prints "file1 file2"
echo file1 file2 | xargs cat    # Prints the CONTENTS of file1 and file2

echo实际上或多或少做了相反的事情:它将其参数转换为 STDOUT(可以通过管道传输到其他命令的 STDIN):

echo file1 file2 | echo    # Prints a blank line, since echo doesn't read from STDIN
echo file1 file2 | xargs echo    # Prints "file1 file2" -- the first echo turns
                                 # them from arguments into STDOUT, xargs turns
                                 # them back into arguments, and the second echo
                                 # turns them back into STDOUT
echo file1 file2 | xargs echo | xargs echo | xargs echo | xargs echo    # Similar,
                                 # except that it converts back and forth between
                                 # args and STDOUT several times before finally
                                 # printing "file1 file2" to STDOUT.

答案2

cat接受输入STDIN,但rm不接受。对于此类命令,您需要xargs逐行迭代STDIN并使用命令行参数执行命令。

答案3

xargs通过最小的例子来理解

xargs在研究 xargs 为什么有用之前,让我们首先通过一些简单的例子来确保我们理解它的作用。

当您执行以下任一操作时:

printf '1 2 3 4' | xargs rm
printf '1\n2\n3\n4' | xargs rm

xargs 解析来自 stdin 的输入字符串,并用空格分隔参数,有点像 Bash,但细节略有不同。特别是,如果您使用xargs -L而不是,空格和换行符的处理方式会有所不同-nhttps://stackoverflow.com/questions/6527004/why-does-xargs-l-yield-the-right-format-while-xargs-n-doesnt/6527308#6527308

因为我们没有使用-L,所以上述两个调用是等效的,并且 xargs 将解析出四个参数:12和。34

然后,xargs 会获取解析出来的参数,并将它们提供给我们要调用的程序。在我们的例子中,它是可执行文件/usr/bin/rm

默认情况下,xargs 不会指定每次要传递多少个参数,除非我们传递一些标志,否则可能会传递多个参数。因此,上述xargs调用可能等同于:

rm 1 2 3 4

或者:

rm 1 2
rm 3 4

或者:

rm 1
rm 2
rm 3
rm 4

并且我们通常不知道上述哪种情况发生了,因为对于rm,最终结果是相同的:文件1234将被删除,所以我们不太关心哪一个xargs在做什么,所以我们只是让它做它的事情。

这可能会对其他程序产生影响,例如/usr/bin/echo,每次调用都会添加一个换行符。

控制一次传递多少个参数

xargs我们可以使用某些标志来控制一次传递多少个参数。

最简单的一个是-n,它限制一次传递的最大参数数量。

/usr/bin/echo然后,我们可以尝试使用而不是 来观察发生了什么/usr/bin/rm,因为echo不同于 ,rm它对 的处理方式echo 1 2不同,echo 1; echo 2因为它会为每个调用添加一个换行符。

考虑到这一点,如果我们运行:

printf '1 2 3 4' | xargs -n2 echo

它每次提供 2 个参数echo,相当于:

echo 1 2
echo 3 4

生成结果:

1 2
3 4

如果我们运行:

printf '1 2 3 4' | xargs -n1 echo

它每次提供一个参数echo,相当于:

echo 1
echo 2
echo 3
echo 4

生成结果:

1
2
3
4

另一种方式是使用-L而不是-n。但只-L-n换行符分隔,而不是空格:https://stackoverflow.com/questions/6527004/why-does-xargs-l-yield-the-right-format-while-xargs-n-doesnt/6527308#6527308

控制参数数量的另一种常见方式是-I暗示-L1,例如:

printf '1\n2\n3\n4\n' | xargs -I% echo a % b

相当于:

echo a 1 b
echo a 2 b
echo a 3 b
echo a 4 b

因此产生:

a 1 b
a 2 b
a 3 b
a 4 b

替代方法及其xargs优越性

现在我们了解了什么xargs是可行的,让我们考虑一下替代方案以及为什么xargs更好。

假设我们有一个文件:

注释.txt

1
2
3
4

代替:

xargs < notes.txt | rm

我们可能想要使用:

rm $(cat notes.txt)

扩展为:

rm 1 2 3 4

然而,这是有问题的,因为Linux 程序的命令行参数有一个最大大小所以如果中的参数太多,它可能会失败notes.txt

xargs知道这一点,并自动明智地拆分参数,以避免同时出现太多参数

而且,像 stdin 这样的流没有最大大小限制,因此可以像这样以任意大小运行。它之所以有效,是因为可以使用系统read()调用一点一点地读取流,而 CLI 参数必须一次性全部加载到虚拟内存中,因此不需要对流大小进行硬性限制。

您可以尝试的另一种简单方法是:

while IFS="" read -r p || [ -n "$p" ]
do
  rm "$p"
done < notes.txt

从:https://stackoverflow.com/questions/1521462/looping-through-the-content-of-a-file-in-bash但这需要大量的输入,并且可能会更慢,因为:

  • 它对/usr/bin/rm每个参数调用一次可执行文件,而不是对一堆参数调用更少的次数
  • bash while与 C 代码相比,循环xargs花费了更多时间

xargs更有趣的是,GNU 版本提供了-P并行操作选项

有关的:https://unix.stackexchange.com/questions/24954/when-is-xargs-needed

相关内容