web-dev-qa-db-ja.com

コマンド「find | grep 'filename'」が「find 'filename'」よりもはるかに遅いのはなぜですか?

私は両方のコマンドを試しましたが、コマンドfind | grep 'filename'は単純なfind 'filename'コマンドよりも何倍も遅いです。

この動作の正しい説明は何でしょうか?

10
yoyo_fun

(GNU find hereと仮定しています)

ただ使う

find filename

wouldは、filename、またはディレクトリの場合はfilename内の名前を返し、その名前が現在のディレクトリに存在しない場合はエラーを返すため、迅速です。これは、ls filenameに似た非常に高速な操作です(ただし、filenameがディレクトリの場合は再帰的です)。

対照的に、

find | grep filename

findallの名前のリストを現在のディレクトリ以下から生成できるようにし、grepがフィルタリングします。これは明らかにはるかに遅い操作になります。

実際にが意図したものは

find . -type f -name 'filename'

これは、現在のディレクトリまたはその下のどこかにある通常のファイルの名前としてfilenameを探します。

これはfind | grep filenameと同じくらい高速(または同等に高速)ですが、grepソリューションは、-path '*filename*'filenameで行うのと同様に、findを、見つかった各名前の完全パスと照合します。


混乱は、findの機能に関する誤解から生じます。

ユーティリティはいくつかのpathsを受け取り、これらのパスの下にあるすべての名前を返します。

次に、ファイル名、パス、タイムスタンプ、ファイルサイズ、ファイルタイプなどに作用するさまざまなテストを使用して、返される名前をrestrictできます。

あなたが言う時

find a b c

findに、abcの3つのパスで使用できるすべての名前をリストするように要求します。これらがたまたま現在のディレクトリにある通常のファイルの名前である場合、これらは返されます。それらのいずれかがたまたまディレクトリの名前である場合、そのディレクトリ内のすべての追加の名前とともに返されます。

私がする時

find . -type f -name 'filename'

これにより、現在のディレクトリ(.)以下のすべての名前のリストが生成されます。次に、-type fを使用して、ディレクトリなどではなく、通常のファイルの名前に制限します。次に、-name 'filename'を使用してfilenameに一致する名前にさらに制限があります。文字列filenameは、*.txtなどのファイル名展開パターンである場合があります(引用符で囲んでください)。

例:

次のコードは、ホームディレクトリで.profileというファイルを「検索」しているようです。

$ pwd
/home/kk
$ find .profile
.profile

しかし実際には、パス.profileにあるすべての名前を返すだけです(名前は1つしかなく、それはこのファイルの名前です)。

次に、cdを1レベル上げて、再試行します。

$ cd ..
$ pwd
/home
$ find .profile
find: .profile: No such file or directory

findコマンドは、.profileという名前のパスを見つけることができなくなりました。

ただし、現在のディレクトリを確認してから返される名前を.profileのみに制限するを指定すると、そこからも検索されます。

$ pwd
/home
$ find . -name '.profile'
./kk/.profile
11
Kusalananda

技術的でない説明:群集の中でジャックを探すことは、群集の中の全員を探すことよりも速く、ジャック以外のすべてを考慮から除外します。

2
S Renalds

私はまだ問題を理解していませんが、もう少し洞察を提供できます。

クサラナンダの場合と同様に、find | grepの呼び出しは、私のシステムでは明らかに高速ですが、あまり意味がありません。最初に、ある種のバッファリングの問題を想定しました。コンソールへの書き込みにより、次のファイル名を読み取るための次のシステムコールまでの時間が遅くなります。パイプへの書き込みは非常に高速です。32バイトの書き込みでも約40 MiB /秒です(私の遅いシステムでは、ブロックサイズが1 MiBの場合は300 MiB /秒)。したがって、パイプ(またはファイル)に書き込むときにfindがファイルシステムからより速く読み取れるため、ファイルパスの読み取りとコンソールへの書き込みの2つの操作を並行して実行できると想定しました(単一のスレッドプロセスとしてfindは、自分の。

findのせいです

2つの呼び出しの比較

:> time find "$HOME"/ -name '*.txt' >/dev/null

real    0m0.965s
user    0m0.532s
sys     0m0.423s

そして

:> time find "$HOME"/ >/dev/null

real    0m0.653s
user    0m0.242s
sys     0m0.405s

findが信じられないほど愚かなことをしていることを示しています(それが何であれ)。 -name '*.txt'の実行ではまったく能力がないことがわかりました。

入力/出力比に依存する可能性があります

書くことがほとんどない場合、find -nameが勝つと思うかもしれません。しかし、istはfindをより恥ずかしく思うだけです。 grepの200Kファイル(13Mのパイプデータ)に対して書き込むものが何もない場合でも失われます。

time find /usr -name lwevhewoivhol

findgrepと同じくらい高速です

findによるnameの愚かさは他のテストには及ばないことがわかりました。代わりに正規表現を使用してください。問題はなくなりました。

:> time find "$HOME"/ -regex '\.txt$' >/dev/null     

real    0m0.679s
user    0m0.264s
sys     0m0.410s

これはバグと考えることができます。バグレポートを提出してくれる人はいますか?私のバージョンはfind(GNU findutils)4.6.0です。

1
Hauke Laging

通知:私はあなたがfind . -name filename(そうでなければ、別のものを探しています; find filenameは実際にはfilenameと呼ばれるパスを調べます。これにはほとんどファイルが含まれていない可能性があるため、非常に迅速に終了します)。


5000個のファイルを保持するディレクトリがあるとします。ほとんどのファイルシステムでは、これらのファイルは実際には tree構造 に格納されているため、特定のファイルをすばやく見つけることができます。

したがって、findに名前の確認のみが必要なファイルを見つけるように依頼すると、findaskに対してthatファイルを検索し、そのファイルのみを検索します、基盤となるファイルシステムへ。これは、マスストレージから非常に少ないページを読み取ります。したがって、ファイルシステムがその価値がある場合、この操作はすべてのエントリを取得するためにツリー全体をトラバースするよりもはるかに高速に実行されます

単純なfindを要求すると、それが実際に行うことですが、ツリー全体をトラバースして読み取ります。すべて。シングル。エントリ。大きなディレクトリの場合、これが問題になる可能性があります(ディスクに多くのファイルを格納する必要があるいくつかのソフトウェアが2つまたは3つのコンポーネントの「ディレクトリツリー」を作成するのはまさにそのためです。ファイル)。

0
LSerni