名前がパターンに一致し、内容がパターンに一致するすべてのファイルに対してコマンドを実行するにはどうすればよいですか?

名前がパターンに一致し、内容がパターンに一致するすべてのファイルに対してコマンドを実行するにはどうすればよいですか?

単語を含むcmdすべて*.cppのファイルとファイルに対して実行したいとします。*.hppFOO

をする発見するファイルが消えます。できることを知っています。

find /path/to/dir -name '*.[hc]pp' -exec grep -l 'FOO' {} +

しかし、cmd私ができるように処理を拡張する正しい方法は何ですか(例:各ファイルごと)。

-exec bash -c '...'私は私ができると書くことができることを知っています。「コンテンツに次のものが含まれているFOO場合はcmd、ファイルから実行してください。」論理...なのに大砲を撃つような感じがする。

答え1

-exec … \;また、内部コマンドが終了ステータス 0 を返す限り、テストは成功します。-exec … +1つのコマンドで多くのパス名を処理でき、常に成功するため、これは便利なテストではありません。

grep -q-exec … \;一致するものがある場合(エラーが検出された場合でも)、終了ステータス0を返し、そうでなければ1を返すので、一緒にうまく機能します。

-exec grep … +必要なコマンドを条件付きで実行するために別のファイルを追加できるように、ファイルを1つずつテストします。-exec grep -q … \;-exec

find /path/to/dir -name '*.[hc]pp' -exec grep -q 'FOO' {} \; -exec …

通常、-exec … \;テストを使用すると、ほぼすべてをテストできるカスタムテストを構築できます。これは、sh -cパイプ、操作可能な変数、シェル条件などを使用してテストを実行できるためです。少し:find -exec sh -c使っても安全ですか?)。

答え2

これらのユーティリティ(およびkshスタイルのプロセス置換をサポートするシェル)のGNU実装を使用すると、次のことができます。

xargs -r0a <(grep -rlZ --include='*.[hc]pp' FOO) cmd --

上で-r(別名--recursive)を使用して操作をgrep実行し(現在のバージョンではそのコマンドに渡されたように動作します)、NULで区切られたファイルパスのリスト()をパイプして(別名)に渡されたパスをパイプします。findgrep-type ffindl-Zxargs-a--arg-file

使い続けたい場合find(たとえば、ファイル名のサフィックスよりも複雑なファイル条件を適用する)、次のことができます。

xargs -r0a <(
  find . -name '*.[hc]pp' -type f -exec grep -lZ FOO {} +
  ) cmd

--ファイルパスは./so not -norで始まるので、ここでは必要ありません+。)

実行はfind ... -exec grep -q FOO {} \; -exec cmd {} +機能しますが、プロセスを分岐して実行することを意味します。これには、各ファイルの共有ライブラリをロードしてリンクすることが含まれます。これは、ここで実行する必要がある作業(いくつかのKiBのテキストを読んで見ること)grepよりもはるかに高価です。したがって、パフォーマンスやリソース使用量が問題であれば、可能であれば避けるのが最善です。grepFOO

GNU System²を使用していないがPOSIX規格の次のバージョンをサポートしてサポートしているfind場合-print0は、以下を使用してファイルパスを報告できます。xargs-r-0perl

find . -name '*.[ch]pp' -type f -print0 |
  xargs -r0 perl -0lne 'if (/FOO/} {print $ARGV; close ARGV}' |
  xargs -r0 cmd

これはstdinから読み取られないと仮定しますが、cmdここでは実装に応じてxargs/ dev / nullで開くか、パイプの読み取り端perlがから継承されて書き込み中ですxargs


<(...)1 rc に似たシェルに pdksh、zsh および bash に基づくもの以外の ksh 実装を含みます。<{...}(...|psub)fish

²しかし、GNUはgrepかつてBSDで使用され(まだまだ一部のBSDで使用されている)、GNUから離れた一部のBSDはそのAPIをコピーしたため、GNU以外の実装がGNU以外の実装をサポートしているgrepことに注意してください。 -標準オプション時には、OpenBSD、FreeBSD、MacOSなどの長いオプションと同等のオプションしかありません。grep-Z--null-Z

答え3

globstarまず、Bashでこのオプションを有効にします。

shopt -s globstar

これで、簡単に次のことができます。

for x in /path/to/dir/**/*.[hc]pp; do
  if grep -q 'FOO' "$x"; then
    ...
  fi
fi

パターン**はゼロ以上のパスコンポーネントと一致します(ディレクトリのみが続く場合/)。

したがって、パターン/path/to/dir/foo.cppも見つかります/path/to/dir/deeply/nested/foo.cpp

答え4

xargsをつなぐ興味深いケース:

find /path/to/dir -name '*.[hc]pp' -print0 | xargs -0 grep -lZ 'FOO' | xargs -0 ...

コマンドによっては、-n 1次に渡す必要があります。第二xargs各ファイルに対してコマンドを実行するために呼び出されます。

このfindコマンドは、一致するファイル名のゼロで区切られたバイナリのリストを生成します。最初のxargsコマンドは、すべてのファイルで一致するコンテンツをテストするために最小数のコマンドをgrep連結し、パターンに一致するゼロで区切られたバイナリファイルのリストを生成し、2番目のxargsコマンドが実行されます。必要なコマンド。

関連情報