我知道该grep
命令,并且正在学习其功能xargs
,因此我通读了这该页面提供了一些如何使用该xargs
命令的示例。
最后一个例子,即示例 10,让我感到困惑。它说“xargs 命令执行 grep 命令来查找所有包含字符串‘stdlib.h’的文件(在 find 命令提供的文件中)”
$ find . -name '*.c' | xargs grep 'stdlib.h'
./tgsthreads.c:#include
./valgrind.c:#include
./direntry.c:#include
./xvirus.c:#include
./temp.c:#include
...
...
...
但是,与简单地使用
$ find . -name '*.c' | grep 'stdlib.h'
?
显然,我仍然在努力弄清楚 xargs 到底在做什么,因此非常感谢您的帮助!
答案1
$ find . -name '*.c' | grep 'stdlib.h'
这将输出 (stdout)* 从 管道传输find
到 (stdin of)*grep 'stdlib.h'
作为文本(即文件名被视为文本)。grep
执行其常规操作并在此文本中找到匹配的行(任何本身包含模式的文件名)。文件的内容永远不会被读取。
$ find . -name '*.c' | xargs grep 'stdlib.h'
这构造了一个命令 grep 'stdlib.h'
每个结果find
都是一个参数 - 因此这将寻找匹配项里面find
(xargs
可以被认为是将其 stdin 转换为给定命令的参数)*找到的每个文件
-type f
在 find 命令中使用,否则您将收到grep
匹配目录的错误。此外,如果文件名中有空格,xargs
将严重出错,因此请使用空分隔符,通过添加-print0
和xargs -0
来获得更可靠的结果:
find . -type f -name '*.c' -print0 | xargs -0 grep 'stdlib.h'
*根据@cat的评论建议添加了这些额外的解释点
答案2
xargs 获取其标准输入并将其转换为命令行参数。
find . -name '*.c' | xargs grep 'stdlib.h'
非常类似于
grep 'stdlib.h' $(find . -name '*.c') # UNSAFE, DON'T USE
只要文件名列表对于单个命令行来说不是太长,就会产生相同的结果。(Linux 在单个命令行上支持兆字节的文本,因此通常不需要 xargs。)
但这两者都很糟糕,因为如果文件名包含空格,则会中断。相反,find -print0 | xargs -0
有效,但
find . -name '*.c' -exec grep 'stdlib.h' {} +
它永远不会将文件名通过管道传输到任何地方:find
将它们分批放入一个大的命令行中并grep
直接运行。
\;
而不是+
对每个文件分别运行 grep,这样会慢得多。不要这样做。但是这+
是 GNU 扩展,因此xargs
如果您不能假设 GNU find,则需要有效地执行此操作。
如果省略xargs
,find | grep
则根据打印的文件名列表进行模式匹配find
。
因此,此时您不妨直接执行find -name stdlib.h
。当然,使用-name '*.c' -name stdlib.h
,您将不会获得任何输出,因为这些模式不能同时匹配,而 find 的默认行为是将规则进行 AND 运算。
less
在流程中的任意一点进行替换,查看管道的任何部分产生的输出。
进一步阅读:http://mywiki.wooledge.org/BashFAQ有一些很棒的东西。
答案3
一般来说,xargs
用于以下情况:您想将|
某个内容(使用符号 )从一个命令传输到另一个命令(Command1 | Command2
),但第一个命令的输出不能正确地作为第二个命令的输入。
这通常发生在第二条命令无法正确处理通过标准输入 (stdin) 输入的数据时(例如:多行作为输入、行设置方式、用作输入的字符、多个参数作为输入、作为输入接收的数据类型等)。举一个简单的例子,测试以下内容:
示例 1:
ls | echo
- 这不会做任何事情,因为echo
不知道如何处理他收到的输入。现在在这种情况下,如果我们使用xargs
它,它将以可以正确处理的方式处理输入echo
(例如:作为单行信息)
ls | xargs echo
ls
- 这将在一行中输出所有信息
示例 2:
假设我在一个名为 go 的文件夹中有多个 goLang 文件。我会用这样的方法查找它们:
find go -name *.go -type f | echo
- 但是如果管道符号在那里并且echo
位于末尾,它将不起作用。
find go -name *.go -type f | xargs echo
- 在这里它可以工作,xargs
但如果我想要find
命令的每个响应都在一行中,我会执行以下操作:
find go -name *.go -type f | xargs -0 echo
find
- 在这种情况下,将显示相同的输出echo
。
诸如 和其他需要更好方式处理输入的命令cp, echo, rm, less
与 一起使用时会受益xargs
。
答案4
xargs
用于(通常)根据文件列表自动生成命令行参数。
因此,考虑使用以下xargs
命令的一些替代方法:
find . -name '*.c' -print0 | xargs -0 grep 'stdlib.h'
有几个理由使用它来代替其他答案中最初未提及的其他选项:
find . -name '*.c' -exec grep 'stdlib.h' {}\;
将为每个文件生成一个grep
进程——这通常被认为是不好的做法,并且如果找到许多文件,可能会给系统带来很大的负载。- 如果文件很多,
grep 'stdlib.h' $(find . -name '*.c')
命令很可能会失败,因为操作的输出$(...)
将超出 shell 的最大命令行长度
正如其他答案中提到的,在这种情况下使用-print0
参数和 xargs 参数的原因是,带有某些字符(例如引号、空格甚至换行符)的文件名仍然能被正确处理。find
-0