在多个文件上对多个关键字进行 Grep:加快速度

在多个文件上对多个关键字进行 Grep:加快速度

我目前在使用 grep 时面临“性能问题”。我试图在许多(想想 Linux 内核存储库大小)文件中找到许多(10,000+)关键字的出现。目标是为每个关键字生成一种索引:

keyword: my_keyword
filepath 1
filepath 2
------
keyword: my_keyword2
...

由于对 shell 脚本相当陌生,我尝试了一些简单的方法:

while read LINE; do
    echo "keyword: "$LINE >> $OUTPUT_FILE
    grep -E -r --include='Makefile*' "($LINE)" . >> "$OUTPUT_FILE"
    echo "-----"
done < $KEYWORD_FILE

这大约需要 45 分钟才能完成,并达到预期结果。

注意:我搜索的所有关键字都位于一个文件中(“KEYWORD_FILE”,因此在任何内容开始之前就已修复),并且可以在搜索开始之前确定要搜索的文件。我尝试在搜索之前存储要搜索的文件列表,如下所示:

file_set=( $(find . -name "Kbuild*" -o -name "Makefile*") )

然后将 grep 调用替换为

echo {$file_set[*]} | parallel -k -j+0 -n 1000 -m grep -H -l -w "\($LINE\)" >> "$OUTPUT_FILE"

大约需要一个小时才能完成……

问题:考虑到我可以使用任何我想要的技术,只要它符合 sh 规范,如何才能“更快”和/或更有效地完成此操作?

也许grep不是使用的工具,而且我的使用parallel是错误的......欢迎任何想法!

答案1

目标是为每个关键字生成一种索引:

然后使用索引软件代替grep。我使用它取得了巨大的成功代码搜索整体上CPAN档案。

答案2

我会编写一个 Perl 脚本(是的,我知道 Python 目前更受欢迎......),它会吞入 10,000 个关键字,读取每个文件,并针对每一行查看其中一些是否匹配。如果匹配,则将其作为文件/行存储在关键字下(散列在这里可以正常工作)。完成该组后,检查哈希,输出每个关键字的结果。

Perl(和Python)是一种(半)编译语言,脚本被编译成内部表示(非常紧凑,易于快速解释),并且这种内部形式得到(一些)“优化器”的喜爱。速度与手工优化的 C 不同,但也不应该慢 10 倍。

最后,你上面的评论击中了要害:你的时间(编写、调试;或获取、构建、学习如何使用)是很多比机器时间更有价值(如果你让它运行过夜,谁在乎它是在晚上 10 点还是早上 6 点完成......)。

答案3

使用 -f 和 -F 开关来 grep 可能会加快速度。 grep 可以相当聪明关于处理多个模式的匹配。

目标是为每个关键字生成一种索引

那么也许解决方案是搜索引擎(例如mnogo)?

答案4

我想到了以下几点:

  1. 您不能为每个关键字认真地 grep 每个文件一次...然后也不会-l / --files-with-matches无用地 grep 直到每个文件的末尾。不幸的是(据我所知)没有--files-with-matches多种模式的组合(至少不是您需要的方式)。这可以在 awk 中完成。 awk 不能递归调用(AFAIK),但可以通过 find 调用大量文件。
  2. 我的经验是,通过首先使用 find 遍历树,可以大大加快访问许多文件(或者更确切地说是它们的索引节点)的操作。我想这会大大减少磁盘头的移动,因为目录条目和索引节点是按块读取的,并且元数据是缓存的。不要让 find 打印整个路径,-printf .就足够了,并将输出写入 /dev/null。
  3. 最快的解决方案可能不是依赖 shell 工具,而是编写一些可以编译的东西(例如带有 JIT 的 Python(编辑:))。这样你就可以优化 I/O(实现 --files-with-matches)和 CPU(只需测试尚未匹配的关键字的每一行)并将文件路径同时附加到每个索引文件(到 tmpfs? );为每个关键字保留一个 FD(假设您的关键字少于 1000 个)。之后您只需连接这些文件即可。即使是类似查找的缓存也可以通过这种方式进行优化:首先读取文件名,直到有 1000 个匹配的文件名,然后读取它们的索引节点(stat() 文件),然后搜索文件,然后读取接下来的 1000 个名称。

相关内容