私は非常に単純な検索を行っていました:
grep -R Milledgeville ~/Documents
そしてしばらくすると、このエラーが発生しました:
grep: memory exhausted
どうすればこれを回避できますか?
私のシステムには10 GBのRAM=があり、いくつかのアプリケーションが実行されているため、単純なgrepがメモリ不足になることに本当に驚いています。~/Documents
は約100GBで、あらゆる種類のファイルが含まれています。
grep -RI
にはこの問題はないかもしれませんが、私もバイナリファイルを検索したいと思います。
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で削除されました
私はいつもします
find ~/Documents | xargs grep -ne 'expression'
たくさんの方法を試しましたが、これが最速であることがわかりました。これはファイル名にスペースを含むファイルをうまく処理しないことに注意してください。これが当てはまることがわかっていて、GNU grepのバージョンがある場合は、次を使用できます。
find ~/Documents -print0 | xargs -0 grep -ne 'expression'
そうでない場合は使用できます:
find ~/Documents -exec grep -ne 'expression' "{}" \;
これは、すべてのファイルに対してgrepをexec
します。
これを回避するにはいくつかの方法があります。
すべてのファイルを一度に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
失われたデータを検索するために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