web-dev-qa-db-ja.com

grep:メモリ不足

私は非常に単純な検索を行っていました:

grep -R Milledgeville ~/Documents

そしてしばらくすると、このエラーが発生しました:

grep: memory exhausted

どうすればこれを回避できますか?

私のシステムには10 GBのRAM=があり、いくつかのアプリケーションが実行されているため、単純なgrepがメモリ不足になることに本当に驚いています。~/Documentsは約100GBで、あらゆる種類のファイルが含まれています。

grep -RIにはこの問題はないかもしれませんが、私もバイナリファイルを検索したいと思います。

42
Nicolas Raoul

2つの潜在的な問題:

  • grep -R(変更されたGNU grepはOS/X 10.8以降で見つかります)を除いて)はシンボリックリンクをたどるため、~/Documentsに100GBのファイルしかない場合でも、たとえば、/へのシンボリックリンクを使用すると、/dev/zeroなどのファイルを含むファイルシステム全体がスキャンされます。grep -rを新しいGNU grepで使用するか、標準の構文を使用します。

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

    (ただし、終了ステータスはパターンが一致したかどうかを反映しないことに注意してください)。

  • grepは、パターンに一致する行を検索します。そのため、メモリに一度に1行をロードする必要があります。 GNU grepは他の多くのgrep実装とは対照的に、読み込む行のサイズに制限がなく、バイナリファイルでの検索をサポートしています。したがって、 、使用可能なメモリよりも大きい非常に大きな行(つまり、2つの改行文字が非常に広い)のファイルがある場合、失敗します。

    これは通常、スパースファイルで発生します。あなたはそれを再現することができます:

    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 {} +
    

    入力をgrepに渡す前に、NUL文字のシーケンスを1つの改行文字に変換します。これは、問題がスパースファイルが原因である場合をカバーします。

    大きなファイルに対してのみ行うことで最適化できます。

    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 {} + \)
    

    ファイルがnotスパースであり、2.6の前にGNU grepのバージョンがある場合、--mmapオプションを使用できます。行は次のようになります。そこにコピーされるのではなく、メモリにmmmapされます。これは、システムが常にページをファイルにページングすることでメモリを再利用できることを意味します。このオプションは、GNU grep 2.6で削除されました

46

私はいつもします

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

たくさんの方法を試しましたが、これが最速であることがわかりました。これはファイル名にスペースを含むファイルをうまく処理しないことに注意してください。これが当てはまることがわかっていて、GNU grepのバージョンがある場合は、次を使用できます。

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

そうでない場合は使用できます:

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

これは、すべてのファイルに対してgrepをexecします。

5
Kotte

これを回避するにはいくつかの方法があります。

  • すべてのファイルを一度にgrepするのではなく、一度に1つのファイルを実行します。例:

    find /Documents -type f -exec grep -H Milledgeville "{}" \;
    
  • どのファイルに単語が含まれているのかを知るだけでよい場合は、grep -l代わりに。 grepは最初のヒット後に検索を停止するため、巨大なファイルを読み続ける必要はありません。

  • 実際のテキストも必要な場合は、2つの別個のgrepsをつなぐことができます。

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

失われたデータを検索するために6 TBのディスクをgrepして、メモリを使い果たしました-エラー。これは他のファイルでも機能するはずです。

私たちが思いついた解決策は、ddを使用してチャンクでディスクを読み取り、チャンクをgreppingすることでした。これはコード(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
3
PHZ.fi-Pharazon