grep:内存耗尽

grep:内存耗尽

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

grep -R Milledgeville ~/Documents

过了一段时间,出现了这个错误:

grep: memory exhausted

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

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

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

答案1

两个潜在的问题:

  • grep -R(除了grepOS/X 10.8 及更高版本上发现的修改后的 GNU)遵循符号链接,因此即使 中只有 100GB 的文件~/Documents,可能仍然存在一个符号链接/,例如,您最终将扫描整个文件系统,包括文件喜欢/dev/zerogrep -r与较新的 GNU 一起使用grep,或使用标准语法:

    find ~/Documents -type f -exec grep Milledgeville /dev/null {} +
    

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

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

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

    truncate -s200G some-file
    grep foo some-file
    

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

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

    在将输入提供给 之前,它将 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 {} + \)
    

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

答案2

我通常这样做

find ~/Documents | xargs grep -ne 'expression'

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

find ~/Documents -print0 | xargs -0 grep -ne 'expression'

如果没有你可以使用:

 find ~/Documents -exec grep -ne 'expression' "{}" \;

这将为exec每个文件执行一个 grep 操作。

答案3

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

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

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

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

      for file in $( grep -Rl Milledgeville /Documents ); do \
          grep -H Milledgeville "$file"; done
    

答案4

我正在 grep 6TB 磁盘来搜索丢失的数据,并得到内存耗尽错误。这也适用于其他文件。

我们提出的解决方案是使用 dd 并 grep 来读取磁盘块。这是代码(big-grep.sh):

#problem: grep gives "memory exhausted" error on 6TB disks
#solution: read it on parts
if [ -z $2 ] || ! [ -e $1 ]; then echo "$0 file string|less -S # greps in chunks"; exit; fi

FILE="$1"
MATCH="$2"

SIZE=`ls -l $1|cut -d\  -f5`
CHUNKSIZE=$(( 1024 * 1024 * 1 )) 
CHUNKS=100 # greps in (100 + 1) x 1MB = 101MB chunks
COUNT=$(( $SIZE / $CHUNKSIZE * CHUNKS ))

for I in `seq 0 $COUNT`; do
  dd bs=$CHUNKSIZE skip=$(($I*$CHUNKS)) count=$(( $CHUNKS+1)) if=$FILE status=none|grep -UF -a --context 6 "$MATCH"
done

相关内容