我正在尝试当前目录中的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
一个人通常grep
个数千个文件?
由于 众议员
答案 0 :(得分: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
$
set -x
允许您查看shell实际如何插入glob,然后将其作为输入传递回命令。 >
指向命令实际执行的行。
您可以看到echo
命令无法解释*
。相反,shell会抓取*
并将其替换为匹配文件的名称。然后,只有echo
命令实际执行命令。
如果您有40K以上的文件,并且grep *
,那么您*
之前将grep
扩展为这些40,000多个文件的名称,甚至有机会执行,以及错误消息 / usr / bin / grep:参数列表太长的来源。
幸运的是,Unix可以解决这个难题:
$ find . -name "*.kaks" -type f -maxdepth 1 | xargs grep -f A01/genes.txt
find . -name "*.kaks" -type f -maxdepth 1
会找到所有*.kaks
个文件,而-depth 1
只会包含当前目录中的文件。 -type f
确保您只选择文件而不是目录。
find
命令将文件名称管道为xargs
,xargs
会将文件名称附加到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
-print0
将导致find
打印出不是由换行符分隔的文件名,而是打印出NUL字符。 -0
的{{1}}参数告诉xargs
文件分隔符不是空格,而是NUL字符。因此,解决了这个问题。
您也可以这样做:
xargs
这将为找到的每个文件而不是$ find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \;
执行grep
,并且只对命令行上可以填充的所有文件运行xargs
。这样做的好处是它完全避免了外壳干扰。但是,效率可能会降低,也可能不会降低效率。
有趣的是尝试并查看哪一个更有效。您可以使用grep
查看:
time
这将执行命令,然后告诉你需要多长时间。使用$ time find . -name "*.kaks" -type f -maxdepth 1 -exec grep -f A01/genes.txt {} \;
和-exec
进行尝试,看看哪个更快。让我们知道你发现了什么。
答案 1 :(得分:7)
您可以将find
与grep
合并为:
find . -maxdepth 1 -name '*.kaks' -exec grep -H -f A01/genes.txt '{}' \; > A01/A01.result.txt
答案 2 :(得分:0)
您可以使用grep
的递归功能:
for i in $(cat A01/genes.txt); do
grep -r $i .
done > A01/A01.result.txt
但是如果您只想选择kaks
个文件:
for i in $(cat A01/genes.txt); do
find . -iregex '.*\.kaks$' -exec grep $i \;
done > A01/A01.result.txt
答案 3 :(得分:0)
在你的外部循环中放入另一个循环:
for f in *.kaks; do
grep -H $i "$f"
done
顺便说一下,您是否有兴趣在每个文件中找到每个事件,或者仅仅搜索字符串存在一次或多次?如果它足够好"要知道字符串出现在那里一次或多次你可以指定" -n 1" grep并且在找到第一个匹配后不会费心读取/搜索文件的其余部分,这可能会节省大量时间。
答案 4 :(得分:0)
以下解决方案对我有效:
问题:
grep -r "example\.com" *
-bash: /bin/grep: Argument list too long
解决方案:
grep -r "example\.com" .
[“在较新版本的grep中,您可以省略“。”,因为它暗示了当前目录。”]