
私は常に構文を探していることを発見します。
find . -name "FILENAME" -exec rm {} \;
-exec
主にその部分が正確にどのように機能するのか理解していないからです。中かっこ、バックスラッシュ、セミコロンはどういう意味ですか?この構文の他のユースケースはありますか?
答え1
この回答は次の部分に分けられます。
- 基本的な使い方
-exec
-exec
とともにsh -c
- 使用
-exec ... {} +
- 使用
-execdir
基本的な使い方-exec
この-exec
オプションは、オプションのパラメータを持つ外部ユーティリティを引数として使用して実行します。
この文字列が{}
指定されたコマンドのどこにでも表示されると、この文字列の各インスタンスは現在処理中のパス名(たとえば./some/path/FILENAME
)に置き換えられます。ほとんどのシェルでは、これら2つの文字を{}
引用符で囲む必要はありません。
コマンドが終わる場所を知るには、コマンドがforで;
終わる必要があります(その後にさらに多くのオプションがある可能性があるため)。シェルからfind
保護するには、orで引用する必要があります。そうでなければ、シェルはそれをコマンドの終わりとして扱います。;
\;
';'
find
はい(\
最初の2行の終わりに連続専用行):
find . -type f -name '*.txt' \
-exec grep -q 'hello' {} ';' \
-exec cat {} ';'
これにより、現在のディレクトリ内または下のパターンと-type f
名前が一致するすべての一般ファイル()が検索されます。次に、その文字列が見つかったファイルに表示されるかどうかを*.txt
テストします(出力はなく、終了状態のみを生成します)。この文字列を含むファイルの場合、ファイルの内容は端末に出力されます。hello
grep -q
cat
それぞれは、と同様に、-exec
見つかったパス名の「テスト」に似ています。コマンドが終了ステータス0(「成功」を示す)を返すと、コマンドの次の部分が考慮され、そうでない場合、コマンドは次のパス名に続きます。これは上記の例では文字列を含むファイルを見つけるために使用されていますが、他のすべてのファイルは無視されます。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つのパラメータを渡します。これはスクリプトに配置され使用さ$0
れます。$1
ひも
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
string text
、$2
string 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(このオプションをサポートしています-t
)にGNUが必要です。
を使用することは、-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
まだデフォルト名の前にプレフィックスを付けます./
。 BSDはfind
そうsfind
ではありません。
例:
find . -type f -name '*.txt' \
-execdir mv -- {} 'done-texts/{}.done' \;
これにより、見つかった各*.txt
ファイルが既存のdone-texts
サブディレクトリに移動されます。ファイルが見つかったディレクトリと同じディレクトリ。.done
サフィックスを追加すると、ファイル名も変更されます。 、デフォルト名の前に。を付けない実装では、--
オプションの終わりを表示する必要があります。シェルが含まれている場合は、完全に含まれていない引数を囲む必要があります。また、すべての実装がそこに拡張されるわけではありません(そうではありません)。find
./
{}
(t)csh
find
{}
sfind
ファイルの新しい名前を設定する-exec
には、見つかったファイルのデフォルト名を取得する必要があるため、これは少しトリッキーです。また、ディレクトリを正しく見つける{}
には、ディレクトリ名が必要です。{}
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 {} +