grep:内存耗尽

Nic*_*oul 45 performance grep memory

我正在做一个非常简单的搜索:

grep -R Milledgeville ~/Documents
Run Code Online (Sandbox Code Playgroud)

一段时间后,出现此错误:

grep: memory exhausted
Run Code Online (Sandbox Code Playgroud)

我怎样才能避免这种情况?

我的系统上有 10GB 的 RAM 并且运行的应用程序很少,所以我真的很惊讶一个简单的 grep 内存不足。~/Documents大约100GB,包含各种文件。

grep -RI 可能没有这个问题,但我也想搜索二进制文件。

Sté*_*las 49

两个潜在的问题:

  • grep -R(除了修改的GNUgrep的OS / X 10.8及以上发现的)如下符号链接,所以即使只有100GB的文件中~/Documents,有可能仍然是一个符号链接/,例如,你最终会扫描整个文件系统,包括文件喜欢/dev/zero。使用grep -r较新的GNU grep,或使用标准的语法:

    find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
    
    Run Code Online (Sandbox Code Playgroud)

    (但是请注意,退出状态不会反映模式是否匹配的事实)。

  • grep查找与模式匹配的行。为此,它必须在内存中一次加载一行。grep与许多其他grep实现相比,GNU对它读取的行的大小没有限制,并支持在二进制文件中搜索。因此,如果您有一个文件的行非常大(也就是说,两个换行符相距很远),并且比可用内存大,那么它就会失败。

    这通常会发生在稀疏文件中。您可以使用以下方法重现它:

    truncate -s200G some-file
    grep foo some-file
    
    Run Code Online (Sandbox Code Playgroud)

    那个很难解决。你可以这样做(仍然使用 GNU grep):

    find ~/Documents -type f -exec sh -c 'for i do
      tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0"
      done' Milledgeville {} +
    
    Run Code Online (Sandbox Code Playgroud)

    这会将 NUL 字符序列转换为一个换行符,然后将输入提供给grep. 这将涵盖问题由稀疏文件引起的情况。

    您可以通过仅对大文件执行此操作来优化它:

    find ~/Documents -type f \( -size -100M -exec \
      grep -He Milledgeville {} + -o -exec sh -c 'for i do
      tr -s "\0" "\n" < "$i" | grep --label="$i" -He "$0"
      done' Milledgeville {} + \)
    
    Run Code Online (Sandbox Code Playgroud)

    如果文件不是稀疏的,并且您拥有grep之前的 GNU 版本2.6,则可以使用该--mmap选项。这些行将被映射到内存中而不是复制到内存中,这意味着系统始终可以通过将页面分页到文件来回收内存。该选项已在 GNU grep2.6中删除

  • @GodricSeer,它可能仍然将文件的很大一部分读入单个缓冲区,但如果它没有在那里找到字符串并且也没有找到换行符,我敢打赌它会将该单个缓冲区保留在内存中并读取下一个缓冲区,因为如果找到匹配项,它将必须显示它。所以,问题还是一样。实际上,200GB 稀疏文件上的 grep 确实会因 OOM 而失败。 (4认同)
  • GNU grep `--null-data` 选项在这里也可能有用。它强制使用 NUL 而不是换行符作为输入行终止符。 (3认同)

Jen*_*y D 5

我可以想出几种方法来解决这个问题:

  • 不要一次 grep 所有文件,而是一次只处理一个文件。例子:

      find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您只需要知道哪些文件包含这些单词,请grep -l改为这样做。由于 grep 将在第一次命中后停止搜索,因此它不必继续读取任何大文件

  • 如果您确实也想要实际文本,您可以将两个单独的 grep 串起来:

      for file in $( grep -Rl Milledgeville /Documents ); do \
          grep -H Milledgeville "$file"; done
    
    Run Code Online (Sandbox Code Playgroud)


Kot*_*tte 5

我通常做

find ~/Documents | xargs grep -ne 'expression'
Run Code Online (Sandbox Code Playgroud)

我尝试了很多方法,发现这是最快的。请注意,这不能很好地处理文件名带有空格的文件。如果您知道是这种情况并且拥有 GNU 版本的 grep,则可以使用:

find ~/Documents -print0 | xargs -0 grep -ne 'expression'
Run Code Online (Sandbox Code Playgroud)

如果没有,您可以使用:

 find ~/Documents -exec grep -ne 'expression' "{}" \;
Run Code Online (Sandbox Code Playgroud)

这将exec是每个文件的 grep。