当使用管道时何时使用xargs?

47

我刚开始学习Bash,正在尝试理解xargs的使用方法,但是我还不太清楚。例如:

history | grep ls

这里我正在搜索我历史记录中的ls命令。在此命令中,我没有使用xargs,它却可以正常工作。

find /etc - name "*.txt" | xargs ls -l

在这个例子中,我必须使用xargs,但我仍然不能理解差异,也无法正确决定何时使用xargs以及何时不使用。

在这个例子中,我必须使用xargs,但我仍然不能理解它与其他工具的区别,并且无法正确地判断何时应该使用xargs以及何时不需要使用。


2
底线是命令行所能接受的大小是有限制的。这个硬编码数字通常为128KiB,请参见什么定义了单个参数的最大命令大小?。当您的命令行超过该大小时,您的替代方法是使用xargs,它将分解并为您处理命令。 - David C. Rankin
6个回答

阿里云服务器只需要99元/年,新老用户同享,点击查看详情
49

xargs可以在你需要将一个命令的输出作为另一个命令的参数时使用。而单独使用管道符号则是将一个命令的输出发送到另一个命令的输入流中。

所以在你的第一个例子中,grep可以接受标准输入的数据,而不需要作为参数传递,因此不需要使用xargs

xargs从标准输入中获取数据并执行一个命令。默认情况下,数据会作为参数附加到命令的末尾。然而,你可以使用一个占位符将其插入到任何位置。传统的占位符是{};使用这个占位符,你的第二个命令可以这样写:

find /etc -name "*.txt" | xargs -I {} ls -l {}
如果您在/etc目录中有3个文本文件,您将获得每个文件的完整目录列表。当然,您也可以简单地编写ls -l /etc/*.txt并节省麻烦。 另一个示例可以让您重命名这些文件,并显示何时需要占位符。{}必须使用两次来指定源和目标名称。
find /etc -name "*.txt" | xargs -I {} mv {} {}.bak
这些都是糟糕的例子,一旦文件名中包含空格,它们就会出错。你可以通过告诉find使用空字符来分隔文件名来解决这个问题。
find /etc -print0 -name "*.txt" | xargs -I {} -0 mv {} {}.bak
我的个人观点是,几乎总是有替代方案来使用xargs(比如find命令的-exec参数,或者进程替换操作符process substitution),学习这些替代方案会更有帮助。

如果您觉得有用的话,我使用的是这个:https://gitlab.com/es20490446e/args - Alberto Salvia Novella
你能解释一下 xargs -I {} 中的 -I {} 是什么意思吗? - Mohammad Faisal
@MohammadFaisal,你有没有读答案的第二段?它指定了占位符。 - miken32

20

如果您在不使用xargs的情况下使用管道符,则实际数据将被传输到下一个命令。另一方面,如果使用xargs的管道符,则实际数据将被视为下一个命令的参数。举个具体的例子,假设您有一个包含a.txtb.txt的文件夹。a.txt只包含一行'hello world!',而b.txt为空。

如果您执行以下操作:

ls | grep txt

你最终将得到输出:

a.txt
b.txt

然而,如果您这样做

ls | xargs grep txt

由于文件a.txt和b.txt中都不包含单词txt,因此您将得到空结果。

如果命令是

ls | xargs grep hello

你将会得到:

hello world!

这是因为使用 xargs 时,ls 给出的两个文件名被作为参数传递给了 grep,而不是实际内容。


ls | grep txt 中,grep 仍然会得到一个文件名(类似于“/proc/1551/fd/0”)作为其参数,然后在该文件中搜索。 - Youjun Hu

4

了解数据流程

虽然管道运算符|将最后一个命令的标准输出重定向到下一个命令的标准输入中,但xargs命令会从标准输入中构建并执行一个命令。

简而言之:

  • | 标准输出 -> 标准输入
  • xargs 标准输入 -> 命令参数

由于xargs需要使用管道运算符的结果作为参数,因此结合起来可以很方便地将标准输出转换为命令参数:

  • | xargs 标准输出 -> 标准输入 (管道) -> 命令参数 (xargs)

何时使用|| xargs

简而言之,这取决于您要执行的命令,因为不同的命令根据其实现方式以不同的方式处理标准输入或命令参数。

换句话说,即使是那些既接受标准输入又接受参数的命令,在你提供其中之一时也可能表现不同,正如下面的例子所示。 但是,如果你想要某种规则,可以考虑以下两点: - 如果你想要处理的数据已经在 stdout 中,应该使用管道操作符 |; - 如果 stdout 包含你想要处理的文件名或者用于更改命令默认行为的参数,应该使用组合 | xargs。 然而,要做好例外情况的准备。

示例

为下一个示例设置场景:

echo "hello_world_content" > "hello_world.txt"
# Creates a file named "hello_world.txt" and
# writes "hello_world_content" inside it

grep 命令:

ls | grep "hello"
# Grep searches for 'hello' in the stdout of the ls command
# Outputs: hello_world.txt


ls | xargs grep "hello"
# Corresponds to: grep 'hello_world.txt'
# Outputs: hello_world_content

echo 命令:

ls | echo
# Outputs a blank line, as echo seems to ignore the stdin
# Outputs:


ls | xargs echo
# Corresponds to: echo 'hello_world.txt'
# Outputs: hello_world.txt

cat 命令:

ls | cat
# Reads and prints the stdout of ls
# Outputs: hello_world.txt


ls | xargs cat
# Corresponds to: cat 'hello_world.txt'
# Outputs: hello_world_content

cat 命令 [第二部分]:

echo "this_file_does_not_exists.txt" | cat
# Reads and prints the stdout of echo
# Outputs: this_file_does_not_exists.txt


echo "this_file_does_not_exists.txt" | xargs cat
# Corresponds to: cat 'this_file_does_not_exists.txt'
# Outputs error: cat: this_file_does_not_exists.txt: No such file or directory

3
简短回答:目前应避免使用xargs。当您编写了几十个或几百个脚本后,再返回到xargs。 命令可以从参数中获取输入(例如rm bad_example),也可以从stdin获取输入(不仅仅是在rm -i is_this_bad_too之后的问题中输入y,还有read answer)。其他命令(如grepsed)将查找参数,并在参数未显示输入时切换到输入。
您的grep示例从stdin读取正常,无需特别处理。
您的ls需要以find的输出作为参数。使用xargs只是一种改变情况的方式。有关xargs的更多信息,请参阅man xargs。替代方法:
find /etc -name "*.txt" -exec ls -l {} \;
find /etc -name "*.txt" -ls
ls -l $(find /etc -name "*.txt" )
ls /etc/*.txt

首先尝试查看当您在 /etc 目录中有一个名为 a nasty filename with spaces.txt 的文件时,哪个命令最好。


2

xargs(1)在读取非NUL分隔的输入时是危险的(破损的、可利用的等)。

如果您正在处理文件名,请使用find-exec [command] {} + 代替。 如果您可以获得NUL分隔的输出,请使用xargs -0


@SaraHamad:您可以通过在命令行中添加“-print0”来获取以null结尾的字符串。请查阅您的man页面。(大多数新版本都有此功能) - shellter
这句话改为IFS= read -r -d '' line不是更好,以确保读取空分隔的输入吗? - Dirk Herrmann
@DirkHerrmann 您是正确的。虽然我是为一般情况而编写的。但根据您的评论,我已将此部分删除。 - Rany Albeg Wein
1
“危险(破损,可利用等)”并不意味着它对于一次性脚本来说不是非常有用的,可以轻松处理数十个文件 :) - Jakub M.
@JakubM。磨成灰尘! - Rany Albeg Wein
显示剩余2条评论

1

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