我正在做一个非常简单的搜索:
grep -R Milledgeville ~/Documents
过了一段时间,出现了这个错误:
grep: memory exhausted
我怎样才能避免这种情况?
我的系统有 10GB RAM,运行的应用程序很少,所以我真的很惊讶一个简单的 grep 会耗尽内存。~/Documents
大约100GB,包含各种文件。
grep -RI
可能没有这个问题,但我也想在二进制文件中搜索。
答案1
两个潜在的问题:
grep -R
(除了grep
OS/X 10.8 及更高版本上发现的修改后的 GNU)遵循符号链接,因此即使 中只有 100GB 的文件~/Documents
,可能仍然存在一个符号链接/
,例如,您最终将扫描整个文件系统,包括文件喜欢/dev/zero
。grep -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
选项。这些行将被映射到内存中,而不是复制到内存中,这意味着系统始终可以通过将页面调出到文件来回收内存。该选项在 GNUgrep
2.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