再帰grep vs find / -type f -exec grep {} \; どちらがより効率的/高速ですか?


70

ファイルシステム全体のどのファイルに文字列が含まれているかを見つけるには、どちらがより効率的ですか:再帰grepまたはexecステートメントでgrepで検索しますか?ファイル拡張子またはファイル名に一致する正規表現を知っている場合、少なくともフィルタリングを行うことができますが、-type fどちらが優れているかを知っている場合、findがより効率的だと思います。GNU grep 2.6.3; find(GNU findutils)4.4.2

例:

grep -r -i 'the brown dog' /

find / -type f -exec grep -i 'the brown dog' {} \;


1
数学/コンピューターサイエンス/アルゴリズムの効率は意見に基づいていません。
グレッグレベンタール

これをチェックしてください。再帰的ではありませんが、どちらが優れているかを理解できます。unix.stackexchange.com/questions/47983/...
ラメシュ

8
@AvinashRaj彼は意見を求めていません。彼 』は、どれがより効率的であるか、および/またはより速いかを尋ねています、どちらが「より良い」か。これは完全に答えられる質問であり、これらの2つのプログラムがどのように仕事をするか、および検索するためにあなたが正確に何を与えるかに依存する単一の具体的な答えがあります。
テルドン

2
-exec {} +フォームが行うフォークの数が少なくなるため、の場合よりも高速になることに注意してください-exec {} \;。正確に同等の出力を得るには、オプションに追加-H(または-h)が必要になる場合がありgrepます。
ミケル

2番目の-rオプションgrep
-qwertzguy

回答:


85

よく分かりません:

grep -r -i 'the brown dog' /*

本当にあなたが意図したものです。これは、隠されていないすべてのファイルとディレクトリでgrepを再帰的に実行することを意味します/(ただし、隠しファイルとそれらの内部のディレクトリはまだ見えます)。

あなたが意味すると仮定:

grep -r -i 'the brown dog' /

注意すべきいくつかの点:

  • すべてのgrep実装がサポートするわけではありません-r。そして、それらの間で動作が異なります:ディレクトリツリーを横断するときにディレクトリへのシンボリックリンクに従うものもあります(つまり、同じファイルで何度も検索したり、無限ループで実行したりする可能性があります)。一部はデバイスファイル(/dev/zeroたとえば、かなり時間がかかります)やパイプやバイナリファイルの内部を調べますが、そうでないものもあります。
  • grepファイルを発見するとすぐにファイルの内部を調べ始めるので効率的です。ただし、ファイル内を検索している間は、検索するファイルをこれ以上探していません(ほとんどの場合、おそらく同様に)

きみの:

find / -type f -exec grep -i 'the brown dog' {} \;

-rここでは意味をなさないものを削除しました)は、grepファイルごとに1 つを実行しているため、非常に非効率的です。;引数を1つだけ受け入れるコマンドにのみ使用してください。さらに、ここでgrepは、1つのファイルのみを検索するため、ファイル名は出力されないため、一致する場所がわかりません。

あなたはデバイスファイル、パイプ、シンボリックリンクの内部を見ていません...、シンボリックリンクをたどっていませんが、あなたは潜在的にのようなものの中を見ているの/proc/memです。

find / -type f -exec grep -i 'the brown dog' {} +

grep可能な限り少ないコマンドが実行されるため、はるかに良いでしょう。最後の実行にファイルが1つしかない場合を除き、ファイル名を取得します。そのためには、以下を使用することをお勧めします。

find / -type f -exec grep -i 'the brown dog' /dev/null {} +

またはGNUでgrep

find / -type f -exec grep -Hi 'the brown dog' {} +

噛むのに十分なファイルが見つかるgrepまで開始されないことに注意してください。そのfindため、初期遅延が発生します。またfind、前のファイルgrepが返されるまで、さらにファイルを検索し続けません。大きなファイルリストを割り当てて渡すと、(おそらく無視できるほどの)影響があるため、全体としては、grep -rシンボリックリンクをたどったり、デバイスの内部を調べたりしない場合よりも全体的に効率が低下する可能性があります。

GNUツールの場合:

find / -type f -print0 | xargs -r0 grep -Hi 'the brown dog'

上記のように、grep実行されるインスタンスは可能な限り少なくfindなりますが、最初のgrep呼び出しが最初のバッチ内を検索している間、さらにファイルを検索し続けます。しかし、それは利点かもしれませんし、そうでないかもしれません。たとえば、回転ハードドライブにデータを保存し、ディスク上の異なる場所に保存されたデータにアクセスするfindgrep、ディスクヘッドが絶えず移動するため、ディスクのスループットが低下します。RAIDのセットアップでは(どこfindgrep異なるディスクにアクセスする場合があります)またはSSDの上で、それが正の違いを作るかもしれません。

RAIDセットアップでは、複数の同時 grep呼び出しを実行することで状況が改善される場合があります。3つのディスクを備えたRAID1ストレージ上のGNUツールを引き続き使用すると、

find / -type f -print0 | xargs -r0 -P2 grep -Hi 'the brown dog'

パフォーマンスが大幅に向上する可能性があります。ただし、2番目のコマンドgrepは、最初のgrepコマンドがいっぱいになるのに十分なファイルが見つかった場合にのみ開始されることに注意してください。そのための-nオプションを追加して、xargsより早く実行することができます(grep呼び出しごとに渡すファイルが少なくなります)。

またxargs、端末デバイス以外に出力をリダイレクトする場合、grepssは出力のバッファリングを開始することに注意してください。これは、これらgrepのsの出力がおそらく誤ってインターリーブされることを意味します。あなたは使用する必要があるだろうstdbuf -oLことを回避する(あなたはまだ非常に長い行(通常> 4KiB)に問題がある場合があります)または、それぞれが別々のファイルでの出力を書き込む必要があり、それらを連結するためにそれらの上に(GNUやFreeBSD上のような利用可能な場合)最後に。

ここで、探している文字列は(正規表現ではなく)固定されているため、-Fオプションを使用すると違いが生じる可能性があります(grep実装が既に最適化する方法を知っているとは限りません)。

大きな違いを生む可能性のあるもう1つのことは、マルチバイトロケールの場合にロケールをCに修正することです。

find / -type f -print0 | LC_ALL=C xargs -r0 -P2 grep -Hi 'the brown dog'

内部を参照しないようにするには/proc/sys...、-xdev検索するファイルシステムを使用して指定します。

LC_ALL=C find / /home -xdev -type f -exec grep -i 'the brown dog' /dev/null {} +

または、明示的に除外するパスを整理します。

LC_ALL=C find / \( -path /dev -o -path /proc -o -path /sys \) -prune -o \
  -type f -exec grep -i 'the brown dog' /dev/null {} +

{}と+の意味を誰かが説明できるようにしたり、説明したりできるとは思いません。exec、grep、または使用しているSolarisのボックスには、manページに何も表示されません。単にシェルがファイル名を連結してgrepに渡すだけですか?

3
@Poldieは、それが明確に記述で説明しています-exec述語、Solarisのmanページ
ステファン・Chazelas

ああ、はい。manページ内を検索している間、{charをエスケープしていませんでした。あなたのリンクは優れています。マニュアルページを読むのはひどいです。

1
RAID1 w / 3ディスク?なんて奇妙な
...-ティンク

1
@tink、はいRAID1は2つ以上のディスク上にあります。2つのディスクと比較して3つのディスクでは、冗長性と読み取りパフォーマンスが向上しますが、書き込みパフォーマンスはほぼ同じです。2枚ではなく3枚のディスクでは、コピーの1つでビットが反転したときに、2枚のディスクで3枚すべてのコピーをチェックすることでどちらが正しいかを判断できるため、エラーを修正することもできます本当に教えて。
ステファンシャゼル16

13

場合*でのgrep通話があなたにとって重要ではありません最初はの唯一のインスタンスとして、より効率的にする必要がありますgrep開始され、そしてフォークは無料ではありません。ほとんどの場合、*それでも高速になりますが、エッジの場合は並べ替えで逆になる可能性があります。

他にもあるかもしれませんfind- grep特に多くの小さなファイルでうまく機能する構造です。大量のファイルエントリとiノードを一度に読み取ると、回転メディアのパフォーマンスが向上する場合があります。

しかし、syscall統計を見てみましょう。

見つける

> strace -cf find . -type f -exec grep -i -r 'the brown dog' {} \;
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.86    0.883000        3619       244           wait4
  0.53    0.004809           1      9318      4658 open
  0.46    0.004165           1      6875           mmap
  0.28    0.002555           3       977       732 execve
  0.19    0.001677           2       980       735 stat
  0.15    0.001366           1      1966           mprotect
  0.09    0.000837           0      1820           read
  0.09    0.000784           0      5647           close
  0.07    0.000604           0      5215           fstat
  0.06    0.000537           1       493           munmap
  0.05    0.000465           2       244           clone
  0.04    0.000356           1       245       245 access
  0.03    0.000287           2       134           newfstatat
  0.03    0.000235           1       312           openat
  0.02    0.000193           0       743           brk
  0.01    0.000082           0       245           arch_prctl
  0.01    0.000050           0       134           getdents
  0.00    0.000045           0       245           futex
  0.00    0.000041           0       491           rt_sigaction
  0.00    0.000041           0       246           getrlimit
  0.00    0.000040           0       489       244 ioctl
  0.00    0.000038           0       591           fcntl
  0.00    0.000028           0       204       188 lseek
  0.00    0.000024           0       489           set_robust_list
  0.00    0.000013           0       245           rt_sigprocmask
  0.00    0.000012           0       245           set_tid_address
  0.00    0.000000           0         1           uname
  0.00    0.000000           0       245           fchdir
  0.00    0.000000           0         2         1 statfs
------ ----------- ----------- --------- --------- ----------------
100.00    0.902284                 39085      6803 total

grepのみ

> strace -cf grep -r -i 'the brown dog' .
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 40.00    0.000304           2       134           getdents
 31.71    0.000241           0       533           read
 18.82    0.000143           0       319         6 openat
  4.08    0.000031           4         8           mprotect
  3.29    0.000025           0       199       193 lseek
  2.11    0.000016           0       401           close
  0.00    0.000000           0        38        19 open
  0.00    0.000000           0         6         3 stat
  0.00    0.000000           0       333           fstat
  0.00    0.000000           0        32           mmap
  0.00    0.000000           0         4           munmap
  0.00    0.000000           0         6           brk
  0.00    0.000000           0         2           rt_sigaction
  0.00    0.000000           0         1           rt_sigprocmask
  0.00    0.000000           0       245       244 ioctl
  0.00    0.000000           0         1         1 access
  0.00    0.000000           0         1           execve
  0.00    0.000000           0       471           fcntl
  0.00    0.000000           0         1           getrlimit
  0.00    0.000000           0         1           arch_prctl
  0.00    0.000000           0         1           futex
  0.00    0.000000           0         1           set_tid_address
  0.00    0.000000           0       132           newfstatat
  0.00    0.000000           0         1           set_robust_list
------ ----------- ----------- --------- --------- ----------------
100.00    0.000760                  2871       466 total

1
ファイルシステム全体を検索する規模では、フォークは無視できます。I / Oは削減したいものです。
ジル

OPからのエラーですが、比較は正しくありません。使用する場合は、の-rフラグを削除する必要があります。発生した数を比較することで、同じファイルを何度も検索したことがわかります。grepfindopen
qwertzguy

1
@qwertzguy、いいえ、これ-rは無害であるべき-type fです。なぜなら、引数はどれもディレクトリではないという保証があるからです。複数のopen()sがで開かれた他のファイルへのダウン可能性が高くなりますgrep(ライブラリ、ローカリゼーションデータ...)(ところで私の答えの編集のためのおかげで)それぞれの呼び出しで
ステファンChazelas

5

SSDを使用していて、シーク時間が無視できる場合、GNUパラレルを使用できます。

find /path -type f | parallel --gnu --workdir "$PWD" -j 8 '
    grep -i -r 'the brown dog' {} 
'

これは、find見つかったものに基づいて、同時に最大8つのgrepプロセスを実行します。

これはハードディスクドライブをスラッシングしますが、SSDはそれに十分に対処する必要があります。


-1

これについてもう1つ考慮すべきことは、次のとおりです。

grepが再帰的に通過する必要があるディレクトリには、システムのnofile設定よりも多くのファイルが含まれますか?(たとえば、開いているファイルハンドルの数。ほとんどのLinuxディストリビューションではデフォルトは1024です)

もしそうなら、grepの特定のバージョンは、最大オープンファイルハンドル設定よりも多くのファイルがあるディレクトリにヒットすると、引数リストが長すぎるエラーで爆破されるので、findは間違いなく進むべき方法です。

ちょうど私の2 ¢。


1
なぜgrep爆撃するのですか?少なくともGNU grep /を使用し-Rて、末尾にパスを付けて使用する場合、単純にディレクトリを反復処理します。シェルは、シェル・グロブを与えない限り、何かを拡大するつもりはありません。したがって、指定された例(/*/では、によって列挙されるサブフォルダーの内容ではなく、物質の内容のみgrepがシェルからの引数として渡されません。
0xC0000022L

さて、OPが再帰的に検索することを求めていたことを考慮して(たとえば、「grep -r -i '茶色の犬' / *」)、GNUのgrep(少なくともバージョン2.9)が:-bash:/ bin / 140,000を超えるサブディレクトリが含まれるディレクトリでOPが使用した正確な検索を使用して、grep:引数リストが長すぎます。
-B.Kaatz
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.