web-dev-qa-db-ja.com

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

ファイルシステム全体で文字列を含むファイルを見つけるには、どちらがより効率的ですか。ファイル拡張子またはファイル名と一致する正規表現がわかっていて、-type fどっちがいい? 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' {} \;

73
Gregg Leventhal

よく分かりません:

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

本当にあなたが意味したことです。これは、/のすべての非表示ではないファイルとdirsでgrepを再帰的に実行することを意味します(ただし、非表示のファイルとその内部のdirsを引き続き確認します)。

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

grep -r -i 'the brown dog' /

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

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

君の:

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

(ここでは意味のない-rを削除しました)ファイルごとに1つのgrepを実行しているため、非常に非効率的です。 ;は、引数を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呼び出しが最初のバッチ内を検索している間に、さらにファイルを検索し続けます。それは利点かもしれませんし、そうでないかもしれません。たとえば、データが回転ハードドライブに保存されている場合、findおよびgrepがディスクの異なる場所に保存されているデータにアクセスすると、ディスクヘッドが常に移動するため、ディスクのスループットが低下します。 RAIDセットアップ(findgrepは異なるディスクにアクセスする可能性があります)またはSSDで、それは良い違いをもたらすかもしれません。

RAIDセットアップで、いくつかのconcurrentgrep呼び出しを実行すると、状況が改善される場合があります。 GNU 3つのディスクを持つRAID1ストレージ上のツールで、

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

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

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

ここで、探している文字列は(正規表現ではなく)固定されているため、-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 {} +
93

grep呼び出しの*が重要でない場合は、grepのインスタンスが1つだけ開始され、フォークが空いていないため、最初のほうが効率的です。ほとんどの場合、*を使用しても高速になりますが、エッジの場合、並べ替えによって逆になる可能性があります。

他の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
14
Hauke Laging

SSDを使用していてシーク時間がごくわずかな場合は、GNU parallelを使用できます。

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

これにより、findの検出結果に基づいて、最大8つのgrepプロセスが同時に実行されます。

これはハードディスクドライブをスラッシュしますが、SSDはそれでかなりうまく対処するはずです。

5
Naftuli Kay