の-execオプションを理解する
find . -name "FILENAME" -exec rm {} \;
主に、-exec
の部分がどのように機能するかが正確にわからないためです。中括弧、円記号、セミコロンの意味は何ですか?他の使用例はありますか?その構文は?
コメント
回答
この回答は、次の部分に分かれています。
-
-exec
-
-exec
をsh -c
と組み合わせて使用する基本的な使用法div> -
-exec ... {} +
- の使用
-execdir
-exec
の基本的な使用法
-exec
オプションは、オプションの引数を持つ外部ユーティリティを使用します。その引数を実行します。
文字列{}
が指定されたコマンドのどこかに存在する場合、その各インスタンスは現在処理されているパス名に置き換えられます(例:./some/path/FILENAME
)。ほとんどのシェルでは、2文字の{}
を引用符で囲む必要はありません。
コマンドfind
がどこで終了するかを知るには、;
で終了する必要があります(後でさらにオプションがある場合があるため) s)。 ;
をシェルから保護するには、\;
または";"
として引用する必要があります。それ以外の場合、シェルはそれをfind
コマンドの終わりとして認識します。
例(\
最初の2行の終わりは、行の続きのみです):
find . -type f -name "*.txt" \ -exec grep -q "hello" {} ";" \ -exec cat {} ";"
これにより、すべての通常のファイルが検索されます(-type f
)現在のディレクトリ内またはその下のパターン*.txt
と名前が一致する名前。次に、grep -q
を使用して、見つかったファイルのいずれかで文字列hello
が発生するかどうかをテストします(出力は生成されず、終了のみです)状態)。文字列を含むファイルの場合、cat
が実行され、ファイルの内容が端末に出力されます。
各-exec
は、-type
やivid = “3d1084b1c1と同様に、find
によって検出されたパス名の「テスト」のようにも機能します。 “>
はそうです。コマンドがゼロ終了ステータス(「成功」を意味する)を返す場合、find
コマンドの次の部分が考慮されます。それ以外の場合、find
コマンドは次のパス名から続行されます。これは、上記の例で、文字列hello
を含むファイルを検索するために使用されますが、他のすべてのファイルは無視します。
上記の例は、2つの最も一般的な使用法を示しています。 -exec
のケース:
- 検索をさらに制限するためのテストとして。
- 見つかったファイルに対して何らかのアクションを実行するパス名(通常、ただし必ずしもそうとは限りませんが、
find
コマンドの最後にあります)。
とsh -c
の組み合わせ
-exec
が実行できるコマンドは、外部ユーティリティに限定されていますオプションの引数付き。-exec
で直接シェルビルトイン、関数、条件、パイプライン、リダイレクトなどを使用することは、sh -c
子シェル。
bash
機能が必要な場合は、iv id =の代わりにbash -c
を使用します。 “73af3270ae”> 。
sh -c
はコマンドラインで指定されたスクリプトを使用して/bin/sh
を実行します。その後に、そのスクリプトへのオプションのコマンドライン引数が続きます。
find
を使用せずに、sh -c
を単独で使用する簡単な例:
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" {} ";"
インテの内部rnalスクリプト、$1
は文字列text
、$2
は文字列txt
と$3
は、find
が見つけたパス名です。パラメータ展開${3%.$1}
はパス名を取得し、パス名からサフィックス.text
を削除します。
または、
/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
とは、外部スクリプトの同じ名前の変数とは異なります。
上記は、と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
は次のように実行されます。可能な限り数回。この最後の例では、coreutilsのGNU mv
が必要です(-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
はベース名の前に./
、BSD find
はそれを行いません)。
例:
find . -type f -name "*.txt" \ -execdir mv {} done-texts/{}.done \;
これにより、見つかった各*.txt
ファイルが、ファイルがあった場所と同じディレクトリにある既存のdone-texts
サブディレクトリに移動します。見つかりました。また、ファイルの名前は、接尾辞.done
を追加することで変更されます。
これは、-exec
は、見つかったファイルのベース名を{}
から取得して、ファイルの新しい名前を作成する必要があるためです。 done-texts
ディレクトリを正しく見つけるには、{}
のディレクトリ名も必要です。
、このようないくつかのことが簡単になります。
-execdir
の代わりに-exec
を使用した対応する操作div>は子シェルを使用する必要があります:
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 {} +
コメント
-
-exec
はプログラムと引数を取り、それを実行します。 シェルのコマンドの中には、プログラムと引数のみで構成されているものもあるが、多くはそうではない。シェルのコマンドにはリダイレクトとパイピングを含めることができる。-exec
はできません(ただし、find
全体をリダイレクトできます)。シェルのコマンドでは; && if
などを使うことができる。-exec
できませんが、-a -o
では実行できます。シェルコマンドは、エイリアスまたはシェル関数、あるいは組み込みのいずれかです。-exec
できない。シェルのコマンドは変数を展開することができる。-exec
はできません(ただし、find
を実行する外部シェルはできます)。シェルコマンドは$(command)
を別の方法で置き換えることができる。-exec
できない。 … - ここではシェルコマンドが間違っていると言っています’
find -exec cmd arg \;
は’シェルを呼び出してシェルコマンドラインを解釈する場合、execlp("sh", "-c", "cmd arg")
ではなく、execlp("cmd", "arg")
を直接実行します(cmd
が組み込まれていない場合、シェルはexeclp("cmd", "arg")
と同等の処理を実行することになります。 - すべてを明確にすることができます
-exec
の後から;
またはivid = “4975d24772″までのfind
引数> は、引数とともに実行するコマンドを構成し、{}
引数の各インスタンスを現在のファイル(;
)、および+
の前の最後の引数としての{}
は、個別の引数としてのファイルのリストに置き換えられました({} +
の場合)。 IOW-exec
は、いくつかの引数を取り、;
または{}
+
。 - @Kusalananda Wouldn ‘最後の例も次の単純なコマンドで機能します:
find . -type f -name '*.txt' -exec sh -c "mv $1 $(dirname $1)/done-texts/$(basename $1).done" sh {} ';'
? - @Atralbはい、それも機能し、最後のコードと同じ効果がありましたが、ループ内で、見つかったファイルごとに1回、見つかったファイルごとに
sh
とmv
の両方を実行します。大量のファイルの場合、著しく遅くなります。
man
ページでも utility_nameまたはargument 2文字のみを含む” {} “は置き換えられますd現在のパス名で、これで十分なようです。さらに、質問と同じように-exec rm {} \;
の例があります。私の時代には、”大きな灰色の壁”、印刷されたページ(紙は保管よりも安かった)。ですから、このトピックに不慣れな人にとってはこれで十分だと思います。あなたの最後の質問はここで尋ねるのは公平です。残念ながら、@ Kusalanandaも私もその答えはありません。