この答えは次の部分にあります。
- の基本的な使い方
-exec
-exec
と組み合わせて使用するsh -c
- を使用して
-exec ... {} +
- を使用して
-execdir
の基本的な使い方 -exec
-exec
オプションは、その引数と実行それとして、オプションの引数を指定して、外部ユーティリティを取ります。
文字列{}
が指定されたコマンドのどこかに存在する場合、その各インスタンスは現在処理中のパス名に置き換えられます(例:)./some/path/FILENAME
。ほとんどのシェルでは、2つの文字を{}
引用符で囲む必要はありません。
コマンドは、それがどこで終了するかを知る;
ためにfor で終了する必要がありますfind
(後でさらにオプションがある場合があるため)。を;
シェルから保護するには、\;
またはとして引用する必要があります';'
。そうしないと、シェルはそれをfind
コマンドの最後として認識します。
例(\
最初の2行の最後は、行の継続のためだけです):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
これにより、現在のディレクトリ以下の-type f
パターン*.txt
に名前が一致するすべての通常ファイル()が検索されます。次に、hello
使用されているファイルのいずれかで文字列が発生するかどうかをテストしますgrep -q
(出力は生成されず、終了ステータスのみが生成されます)。文字列を含むファイルについてcat
は、ファイルの内容を端末に出力するために実行されます。
また、それぞれがで-exec
見つけたパス名の「テスト」のfind
よう-type
に-name
機能します。コマンドがゼロの終了ステータス(「成功」を示す)を返す場合、コマンドの次の部分find
が考慮されます。それ以外の場合、find
コマンドは次のパス名で続行されます。これは、上記の例で文字列を含むファイルを検索するために使用されますが、hello
他のすべてのファイルを無視します。
上記の例は、次の2つの最も一般的な使用例を示しています-exec
。
- さらに検索を制限するテストとして。
- 見つかったパス名に対して何らかのアクションを実行します(通常、
find
コマンドの最後に、ただし必ずしもそうではありません)。
-exec
と組み合わせて使用するsh -c
-exec
実行できるコマンドは、オプションの引数を持つ外部ユーティリティに制限されています。シェルビルトイン、関数、条件、パイプライン、リダイレクトなどを直接使用すること-exec
は、sh -c
子シェルのようなものでラップしない限り不可能です。
bash
機能が必要な場合はbash -c
、の代わりに使用しsh -c
ます。
sh -c
/bin/sh
コマンドラインで指定されたスクリプトを使用して実行し、その後にそのスクリプトへのオプションのコマンドライン引数が続きます。
sh -c
なしで単独で使用する簡単な例find
:
sh -c 'echo "You gave me $1, thanks!"' sh "apples"
これにより、子シェルスクリプトに2つの引数が渡されます。
文字列sh
。これは$0
スクリプト内で利用でき、内部シェルがエラーメッセージを出力する場合、この文字列をプレフィックスとして付けます。
引数はapples
として利用できる$1
スクリプト内、および複数の引数があった場合、これらはとして利用されているでしょう$2
、$3
彼らはまた、リストに利用できるようになるなど"$@"
(を除く$0
どのの一部ではありません"$@"
)。
これはと組み合わせ-exec
て使用すると便利です。これにより、で見つかったパス名に作用する任意の複雑なスクリプトを作成できるようになりますfind
。
例:特定のファイル名の接尾辞を持つすべての通常のファイルを検索し、そのファイル名の接尾辞を他の接尾辞に変更します。接尾辞は変数に保持されます。
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c 'mv "$3" "${3%.$1}.$2"' sh "$from" "$to" {} ';'
内部スクリプト内で、$1
文字列になりtext
、$2
文字列になりますtxt
し、$3
何でもパス名になりfind
、私たちのために発見しました。パラメータ展開で${3%.$1}
は、パス名を取得し、そのサフィックスを削除します.text
。
または、dirname
/ を使用しbasename
ます。
find . -type f -name "*.$from" -exec sh -c '
mv "$3" "$(dirname "$3")/$(basename "$3" ".$1").$2"' sh "$from" "$to" {} ';'
または、内部スクリプトに変数を追加して:
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2; pathname=$3
mv "$pathname" "$(dirname "$pathname")/$(basename "$pathname" ".$from").$to"' sh "$from" "$to" {} ';'
この最後のバリエーションでは、変数from
とto
子シェルの変数は、外部スクリプトの同じ名前の変数とは異なることに注意してください。
上記から、任意の複雑なスクリプトを呼び出す正しい方法である-exec
とfind
。のfind
ようなループで使用する
for pathname in $( find ... ); do
エラーが発生しやすくエレガントではありません(個人的な意見)。空白でファイル名を分割し、ファイル名のグロビングを呼び出しますfind
。また、ループの最初の反復を実行する前に、シェルに完全な結果を展開させます。
こちらもご覧ください:
を使用して -exec ... {} +
;
最後には、置き換えてもよいです+
。これによりfind
、見つかったパス名ごとに1回ではなく、できるだけ多くの引数(見つかったパス名)で指定されたコマンドが実行されます。 文字列{}
は+
、これが機能する直前に発生する必要があります。
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} +
ここでfind
は、結果のパス名を収集cat
し、できるだけ多くのパス名を一度に実行します。
find . -type f -name "*.txt" \
-exec grep -q "hello" {} ';' \
-exec mv -t /tmp/files_with_hello/ {} +
ここでも同様に、mv
可能な限り数回実行されます。この最後の例ではmv
、coreutilsのGNUが必要です(-t
オプションをサポートしています)。
を使用すること-exec sh -c ... {} +
は、任意の複雑なスクリプトで一連のパス名をループする効率的な方法でもあります。
基本はを使用する場合と同じです-exec sh -c ... {} ';'
が、スクリプトはより長い引数リストを取ります。これらは"$@"
、スクリプト内でループオーバーすることでループオーバーできます。
ファイル名の接尾辞を変更する最後のセクションの例:
from=text # Find files that have names like something.text
to=txt # Change the .text suffix to .txt
find . -type f -name "*.$from" -exec sh -c '
from=$1; to=$2
shift 2 # remove the first two arguments from the list
# because in this case these are *not* pathnames
# given to us by find
for pathname do # or: for pathname in "$@"; do
mv "$pathname" "${pathname%.$from}.$to"
done' sh "$from" "$to" {} +
を使用して -execdir
また、-execdir
(ほとんどのfind
バリアントで実装されていますが、標準オプションではありません)。
これ-exec
は、見つかったパス名のディレクトリを現在の作業ディレクトリとして指定されたシェルコマンドが実行され、見つかったパス名{}
のベース名をパスなしで実行するという違いがあります(ただし、GNU find
はベース名の先頭に./
、find
それはしません)。
例:
find . -type f -name '*.txt' \
-execdir mv {} done-texts/{}.done \;
これにより、見つかった各ファイルが、ファイルが見つかった場所と同じディレクトリ内の*.txt
既存のdone-texts
サブディレクトリに移動します。また、ファイルにサフィックス.done
を追加することにより、ファイルの名前が変更されます。
これは、ファイルの新しい名前を作成するため-exec
に、見つかったファイルのベース名を取得する必要があるため、少しややこしいでしょう{}
。ディレクトリを適切{}
に見つけるには、ディレクトリ名fromも必要done-texts
です。
で-execdir
、これらのようないくつかのことがより簡単になります。
の-exec
代わりに使用する対応する操作で-execdir
は、子シェルを使用する必要があります。
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "$( dirname "$name" )/done-texts/$( basename "$name" ).done"
done' sh {} +
または、
find . -type f -name '*.txt' -exec sh -c '
for name do
mv "$name" "${name%/*}/done-texts/${name##*/}.done"
done' sh {} +