単語を含むcmd
すべて*.cpp
のファイルとファイルに対して実行したいとします。*.hpp
FOO
をする発見するファイルが消えます。できることを知っています。
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で区切られたファイルパスのリスト()をパイプして(別名)に渡されたパスをパイプします。find
grep
-type f
find
l
-Z
xargs
-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
よりもはるかに高価です。したがって、パフォーマンスやリソース使用量が問題であれば、可能であれば避けるのが最善です。grep
FOO
GNU System²を使用していないがPOSIX規格の次のバージョンをサポートしてサポートしているfind
場合-print0
は、以下を使用してファイルパスを報告できます。xargs
-r
-0
perl
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
コマンドが実行されます。必要なコマンド。