【问题标题】:grep extract specific pattern from a large file of 190GBgrep 从 190GB 的大文件中提取特定模式
【发布时间】:2020-06-02 11:21:36
【问题描述】:

我需要从一个 190GB 的大平面文件(仅错误日志)中提取电子邮件地址,并将其剪切成 5mb 的文件。 (有 152,353,216 行)

grep 命令运行良好,但内存很快就会饱和,我最终会出错。

文件的内容没有格式化,所以我必须使用正则表达式。

grep -r -E -h -o "\b(pattern)\b" /dir/* > outs.txt

如何逐个处理文件?

【问题讨论】:

  • 您遇到了什么错误?我很好奇为什么内存消耗会增加,以及是什么让您认为问题与内存有关。
  • @TomFenech grep 在分析了 20,000 个文件后最终不再找到模式,并且只显示文件的名称。处理在 RAID10 中的 NAS 上启动。

标签: linux shell unix command-line grep


【解决方案1】:

使用xargs 分别对每个文件(而不是所有文件)执行 grep 命令

ls -1 /dir/ | xargs -n 1 -I '{}' grep -r -E -h -o "\b(pattern)\b" '{}' > outs.txt

-n 1 标志指示 xargs 在每个文件上运行一个进程。

-I '{}' 参数指示 xargs 将“{}”替换为文件名。

也就是说,如果/dir包含file1file2...,则依次执行

grep -r -E -h -o "\b(pattern)\b" /dir/file1
grep -r -E -h -o "\b(pattern)\b" /dir/file2
grep -r -E -h -o "\b(pattern)\b" /dir/file3...

【讨论】:

  • 为什么使用lsxargs 而不仅仅是问题中的/dir/*
  • 不明白; « 因为 xargs 的`-n 1` 选项 » 似乎是您问题的一个很好的答案,但也许我错过了什么
  • 当我问这个问题时,仍然不清楚 OP 到底有什么错误。由于已澄清该问题与一次将太多文件传递给 grep 有关,那么这可能会有所帮助,尽管调用这么多 grep 实例会很慢。我也不确定使用 ls -1 | xargs 而不是 for 循环是否有任何优势。
  • 使用xargs 的优点是可以在ls(或find)命令生成后立即处理每个文件。相比之下,for 循环必须生成整个文件列表,然后然后对其进行处理。它涉及内存和性能影响。但是,我必须承认我没有进行基准测试。
【解决方案2】:

处理所有文件的最简单(但可能不是最快)的方法是使用循环逐个处理:

for file in /dir/*; do
  grep -r -E -h -o '\b(pattern)\b' "$file"
done > outs.txt

不过,启动所有这些 greps 的开销可能相当大,所以也许您可以使用 xargs 来提供帮助:

find /dir/ -maxdepth 1 -type f -print0 |
  xargs -0 -n 1000 grep -r -E -h -o '\b(pattern)\b' > outs.txt

这使用find 生成dir 中的文件列表,并将它们安全地传递给xargs,由空字节\0 分隔(保证不在文件名中的字符)。 xargs 然后将文件以 1000 个批量传递给 grep

(我假设您在这里有findxargs 的GNU 版本,用于find -print0xargs -0

【讨论】:

    【解决方案3】:

    根据您的数据、磁盘性能和 CPU,您可能会使用 GNU Parallel 做得更好。如果您使用 --pipepart 选项,它还会为您拆分 190GB 文件,而无需创建临时文件。

    所以,我使用 Perl 创建了一个包含 100000000 行的 5GB 文件,如下所示:

    perl -E 'for($i=0;$i<100000000;$i++){say "Line $i,field2,field3,junk,junk,junk",int rand 1000000}' > BigBoy.txt
    

    前 3 行如下所示:

    Line 0,field2,field3,junk,junk,junk514649
    Line 1,field2,field3,junk,junk,junk257773
    Line 2,field2,field3,junk,junk,junk203414
    

    然后我在产生 88 行输出的文件上将 grep 计时为 58 秒:

    time grep "junk426888$" BigBoy.txt
    

    然后我将 GNU Parallel 计时为 11 秒以获取相同的输出:

    time parallel -a BigBoy.txt --pipepart --block -1 grep "junk426888$"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-27
      • 1970-01-01
      • 2014-06-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多