我正在尝试做一些非常简单的事情;来自列表的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个内核按文件分割作业进行搜索,看起来可能需要一周时间。
有类似的问题:
这里虽然他们在不同的平台上,但我想可能还有其他可能帮助我。 或fgrep可能更快(但我现在正在测试它似乎有点慢) 任何人都可以看到更快的方法吗? 提前谢谢
答案 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
。