Gregging 大文件性能

Gregging 大文件性能

我有 FILE_A 超过 300K 行,FILE_B 超过 30M 行。我创建了一个 bash 脚本,该脚本对 FILE_A 中的每一行进行 grep 到 FILE_B 中,并将 grep 的结果写入新文件。

整个过程需要5个多小时。

我正在寻找您是否认为有任何方法可以提高我的脚本性能的建议。

我使用 grep -F -m 1 作为 grep 命令。 FILE_A 看起来像这样:

123456789 
123455321

FILE_B 是这样的:

123456789,123456789,730025400149993,
123455321,123455321,730025400126097,

因此,在 bash 中,我有一个 while 循环,它会选择 FILE_A 中的下一行并在 FILE_B 中对其进行 grep 。当在 FILE_B 中找到该模式时,我将其写入 result.txt。

while read -r line; do
   grep -F -m1 $line 30MFile
done < 300KFile

预先非常感谢您的帮助。

答案1

性能的关键是只读取一次大文件。

您可以通过将多个模式放在单独的行上来将它们传递给 grep。这通常是通过告诉 grep 从文件中读取模式来完成的:

grep -F -f 300KFile 30MFile

这会按照大文件的顺序输出匹配项,并且仅打印一次匹配多个模式的行。此外,这会在行中的任何位置查找模式;例如,如果模式文件包含1234,则123456,345678,2348962342和等478912,1211138,1234行将匹配。

您可以通过预处理模式来限制精确的列匹配。例如,如果模式不包含任何特殊字符()?*+\|[]{}

<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile

如果只保留每个模式的第一个匹配很重要,请进行第一次传递以仅提取上述相关行,然后在 awk 或 perl 中进行第二次传递以跟踪已经看到的模式。

<300KFile sed -e 's/^/(^|,)/' -e 's/$/($|,)/' |
grep -E -f - 30MFile |
perl -l -F, -ape '
    BEGIN {
        open P, "300KFile" or die;
        %patterns = map {chomp; $_=>1} <P>;
        close P;
    }
    foreach $c (@F) {
        if ($patterns{$c}) {
            print;
            delete $patterns{$c};
        }
    }
'

答案2

你能运行以下命令吗?

grep -Ff FILE_A FILE_B > FILE_C

现在您只能在文件 A 和 C 上运行脚本。

更新:等等...它会保留顺序吗?

另一个更新:需要进行更多处理才能保持订单。这给了我与你的原始脚本相同的结果。在 FILE_A 中的 300K 行和 FILE_B 中仅 300K 行上进行测试,分别为 125 分钟和 14 秒。

#! /bin/bash
grep -Ff FILE_A FILE_B > FILE_B_TMP
grep -oFf FILE_A FILE_B_TMP > FILE_A_SHUFF
grep -Ff FILE_A_SHUFF FILE_A > FILE_A_TMP

while read -r line; do
   grep -F -m1 "$line" FILE_B_TMP
done < FILE_A_TMP > result.txt

答案3

我相信comm可以做出更好的性能:

comm -12 300KFile <(sed 's/,.*//' 30MFile)

附注我不确定 300KFile 中的字符串是否应该与30M 文件中的123123字符串匹配。gdwyedg,123123,hfsjdkfh正如在您的脚本中它匹配,但在我的脚本中它不匹配。

答案4

我相信基于 grep 的解决方案仍然需要将 FILE_A 中的每条记录与 FILE_B 中的每条记录进行比较。由于 FILE_A 中至少有 N-1 条记录与 FILE_B 中的特定记录不匹配,因此这种方法存在大量冗余。另一方面,如果对文件进行排序,则可以在每次比较时放弃大量测试。因此,类似......

#!/bin/bash

# NB a faster solution would be to sort the smaller file in a seperate process
# you might also want to set a buffer size for large files
sort $1 > /tmp/$$.a
sort $2 > /tmp/$$.b

join -j1 -t',' /tmp/$$.a /tmp/$$.b

rm -f /tmp/$$.?

(未测试)

但请注意,条目的顺序将被更改,这预先假设您想要匹配 FILE_B 中的特定数据列,并且排序也会引入开销 - 但对于这些文件大小,结果应该更快。

相关内容