如何grep大量的文件?

upe*_*dra 9 bash grep

我正在尝试grep在当前目录中的40k文件,我收到此错误.

for i in $(cat A01/genes.txt); do grep $i *.kaks; done > A01/A01.result.txt
-bash: /usr/bin/grep: Argument list too long
Run Code Online (Sandbox Code Playgroud)

一般如何grep成千上万的文件?

谢谢Upendra

Dav*_* W. 29

这让大卫伤心......

到目前为止,每个人都错了(除了anubhava).

Shell脚本与任何其他编程语言都不同,因为对行的大部分解释来自shell在实际执行命令之前插入它们的强大功能.

我们来看一些简单的事情:

$ set -x
$ ls
+ ls
bar.txt foo.txt fubar.log
$ echo The text files are *.txt
echo The text files are *.txt
> echo The text files are bar.txt foo.txt
The text files are bar.txt foo.txt
$ set +x
$
Run Code Online (Sandbox Code Playgroud)

set -x让你看到的外壳实际上是如何插值的水珠,然后通过该回命令输入.>指向实际执行的行的点.

你可以看到该echo命令没有解释*.相反,shell抓取*并用匹配文件的名称替换它.然后,只有这样echo命令才能实际执行命令.

如果你有40K以上的文件,你就grep *可以将它扩展*到那些40,000多个文件的名称,grep甚至有机会执行,这就是错误消息/ usr/bin/grep:参数列表太长的地方了来自(哪里.

幸运的是,Unix有办法解决这个难题:

$ find . -name "*.kaks" -type f -maxdepth 1 | xargs grep -f A01/genes.txt
Run Code Online (Sandbox Code Playgroud)

find . -name "*.kaks" -type f -maxdepth 1会发现所有的*.kaks文件,并且-depth 1将只包括在当前目录下的文件.在-type f确保你只拿起文件,而不是一个目录.

find命令通过管道将文件转换成的名称xargsxargs将附加文件的名称grep -f A01/genes.txt命令.但是,xargs有一个技巧.它知道命令行缓冲区的长度,并grep在命令行缓冲区已满时执行,然后将另一系列文件传递给grep.这样,grep执行可能会执行三次或十次(取决于命令行缓冲区的大小),并且我们使用了所有文件.

不幸的是,xargs使用空格作为文件名的分隔符.如果您的文件包含空格或制表符,则会遇到问题xargs.幸运的是,还有另一种解决方法:

$ find . -name "*.kaks" -type f -maxdepth 1 -print0 | xargs -0 grep -f A01/genes.txt
Run Code Online (Sandbox Code Playgroud)

-print0将导致find打印出不是由换行符分隔的文件的名称,而是打印出由NUL字符分隔的文件的名称.在-0为参数xargs告诉xargs该文件分隔符不是空白,但NULL字符.因此,解决了这个问题.

你也可以这样做:

$ find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \;
Run Code Online (Sandbox Code Playgroud)

这将为grep找到的每个文件执行for而不是为命令行上的所有文件xargs运行grep.这样做的好处是它完全避免了外壳干扰.但是,效率可能会降低,也可能不会降低.

有趣的是试验并看哪哪个更有效.你可以用time来看:

$ time find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \;
Run Code Online (Sandbox Code Playgroud)

这将执行命令然后告诉你它花了多长时间.尝试使用-exec和,xargs并看看哪个更快.让我们知道你发现了什么.


anu*_*ava 7

您可以结合find使用grep这样的:

find . -maxdepth 1 -name '*.kaks' -exec grep -H -f A01/genes.txt '{}' \; > A01/A01.result.txt
Run Code Online (Sandbox Code Playgroud)