在大型数据集上使用grep或fgrep进行非常慢的循环

时间:2013-01-03 16:39:43

标签: bash loops grep

我正在尝试做一些非常简单的事情;来自列表的grep,字符串的完全匹配,在目录中的文件:

#try grep each line from the files
for i in $(cat /data/datafile); do 
LOOK=$(echo $i);
fgrep -r $LOOK /data/filestosearch >>/data/output.txt
done

与grep匹配的文件有2000万行,目录有~600个文件,共有~40万行 我可以看到这将是缓慢但我们估计需要7年。即使我在HPC上使用300个内核按文件分割作业进行搜索,看起来可能需要一周时间。

有类似的问题:

Loop Running VERY Slow

Very slow foreach loop

这里虽然他们在不同的平台上,但我想可能还有其他可能帮助我。 或fgrep可能更快(但我现在正在测试它似乎有点慢) 任何人都可以看到更快的方法吗? 提前谢谢

5 个答案:

答案 0 :(得分:5)

听起来像-f的{​​{1}}标志适合这里:

grep

所以-f FILE, --file=FILE Obtain patterns from FILE, one per line. The empty file contains zero patterns, and therefore matches nothing. (-f is specified by POSIX.) 已经可以做你的循环正在做的事了,你可以用以下代码替换循环:

grep

现在我不确定2000万个模式的性能,但至少你没有以这种方式启动2000万个进程,所以它可能要快得多。

答案 1 :(得分:2)

正如马丁在他的回答中所说,你应该使用-f选项而不是循环。我认为它应该比循环更快。

此外,这看起来像是GNU parallel的一个很好的用例。查看this answer以获取用法示例。它看起来很难,但实际上很容易设置和运行。

除此之外,如果只有一个匹配的字符串,那么4000万行对于grep来说应该不是什么大问题。它应该可以在任何体面的机器上一两分钟完成。我在我的笔记本电脑上测试了200万行需要6秒。所以40密耳线应该需要2分钟。

问题在于有2000万个字符串需要匹配。我认为它必须耗尽内存或其他东西,特别是当你在不同的目录上运行它的多个实例时。你能尝试拆分输入的匹配列表文件吗?例如,尝试将其拆分为每个100000字的块。

编辑:刚刚在我的机器上试过并行。真是太棒了。它会自动将grep拆分为多个内核和多台计算机。

答案 2 :(得分:1)

这是提高速度的一种方法:

while read i
do
    LOOK=$(echo $i)
    fgrep -r $LOOK /deta/filetosearch >> /data/output.txt
done < /data/datafile

执行该操作for i in $(cat /data/datafile)时,首先会生成另一个进程,但该进程必须先删除所有这些行,然后再运行其余的脚本。此外,您很可能会超载命令行并最终丢失一些文件。

通过使用q while read循环并重定向来自/data/datafile的输入,您无需生成shell。此外,您的脚本将立即开始阅读while循环,而无需先删除整个/data/datafile

如果$i是目录列表,并且您对下面的文件感兴趣,我想知道find是否可能比fgrep -r快一点。

同时阅读i     做         LOOK = $(echo $ i)         找到$ i -type f | xargs fgrep $ LOOK&gt;&gt; /data/output.txt     完成&lt; /数据/数据文件

xargs将获取find的输出,并在单个fgrep下运行尽可能多的文件。如果这些目录中的文件名包含空格或其他奇怪字符,则xargs可能会很危险。您可以尝试(取决于系统),如下所示:

find $i -type f -print0 | xargs --null fgrep $LOOK >> /data/output.txt

在Mac上它是

find $i -type f -print0 | xargs -0 fgrep $LOOK >> /data/output.txt

正如其他人所说,如果你有GNU版本的grep,你可以给它-f标志并包含你的/data/datafile。然后,您可以完全消除循环。

另一种可能性是切换到实际运行速度比shell快的Perl或Python,并为您提供更多的灵活性。

答案 3 :(得分:0)

您可以编写perl / python脚本,它将为您完成工作。使用外部工具执行此操作时,它会保存您需要执行的所有操作。

另一个提示:您可以在一个正则表达式中组合您要查找的字符串。 在这种情况下,grep将只对所有组合线进行一次传递。

示例:

而不是

for i in ABC DEF GHI JKL
do
grep $i file >> results
done

你可以做到

egrep "ABC|DEF|GHI|JKL" file >> results

答案 4 :(得分:0)

由于您正在搜索简单字符串(而不是正则表达式),因此您可能希望使用comm

comm -12 <(sort find_this) <(sort in_this.*) > /data/output.txt

它占用的内存很少,而grep -f find_this可以吞噬“find_this”大小的100倍。

在8核上,这些文件需要100秒:

$ wc find_this; cat in_this.* | wc
3637371   4877980 307366868 find_this
16000000 20000000 1025893685

请务必拥有sort的合理新版本。它应该支持--parallel