7

我正在尝试做一些非常简单的事情;在目录中的文件上从列表中 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 个文件,总共 ~4000 万行我可以看到这会很慢,但我们估计需要 7 年。即使我在我们的 HPC 上使用 300 个内核将作业按文件拆分以进行搜索,看起来也可能需要一周多的时间。

有类似的问题:

循环运行非常慢

非常慢的foreach循环

在这里,尽管它们位于不同的平台上,但我认为如果有其他可能对我有帮助。或 fgrep 可能更快(但似乎有点慢,因为我现在正在测试它)谁能看到更快的方法来做到这一点?先感谢您

4

5 回答 5

5

听起来像这里的-f标志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已经可以做你的循环正在做的事情,你可以将循环替换为:

grep -F -r -f /data/datafile /data/filestosearch >>/data/output.txt

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

于 2013-01-03T16:50:25.483 回答
2

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

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

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

问题在于有 2000 万个字符串要匹配。我认为它一定是内存不足或什么的,特别是当你在不同的目录上运行它的多个实例时。您可以尝试拆分输入匹配列表文件吗?例如,尝试将其分成 100000 个单词的块。

编辑:刚刚在我的机器上尝试并行。这是惊人的。它会自动将 grep 拆分到几个核心和几台机器上。

于 2013-01-03T17:03:12.330 回答
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),您首先会生成另一个进程,但该进程必须在运行脚本的其余部分之前找出所有这些行。另外,很有可能您会超载命令行并最终丢失一些文件。

通过使用 qwhile read循环并从 重定向输入/data/datafile,您无需生成 shell。另外,您的脚本将立即开始阅读while循环,而无需先找出整个/data/datafile.

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

在阅读时我做 LOOK=$(echo $i) find $i -type f | xargs fgrep $LOOK >> /data/output.txt 完成 < /data/datafile

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. 然后,您可以完全消除循环。

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

于 2013-01-03T17:40:12.340 回答
1

由于您正在搜索简单的字符串(而不是正则表达式),您可能需要使用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.

于 2013-01-10T10:52:43.467 回答
0

您可以编写 perl/python 脚本,它会为您完成这项工作。当您使用外部工具执行此操作时,它可以节省您需要做的所有分叉。

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

例子:

代替

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

你可以做

egrep "ABC|DEF|GHI|JKL" file >> results
于 2013-01-03T16:47:01.463 回答