の-execオプションを理解する

find . -name "FILENAME" -exec rm {} \; 

主に、-execの部分がどのように機能するかが正確にわからないためです。中括弧、円記号、セミコロンの意味は何ですか?他の使用例はありますか?その構文は?

コメント

  • @Philippos:私はあなたの主張を理解しています。manページは参照である、つまりそれらに役立つことを覚えておいてください。構文を調べるための問題を理解している。トピックに不慣れな人にとって、彼らはしばしば不可解で形式的で有用である。受け入れられた答えは、manページのエントリの約10倍の長さであることがわかる。その’には理由があります。
  • 古いPOSIX manページでも utility_nameまたはargument 2文字のみを含む” {} “は置き換えられますd現在のパス名で、これで十分なようです。さらに、質問と同じように-exec rm {} \;の例があります。私の時代には、”大きな灰色の壁”、印刷されたページ(紙は保管よりも安かった)。ですから、このトピックに不慣れな人にとってはこれで十分だと思います。あなたの最後の質問はここで尋ねるのは公平です。残念ながら、@ Kusalanandaも私もその答えはありません。
  • @Philippos :-)ああ、それは、”エキサイティングな”は my チャートのスケールから外れています。
  • Comeon @ Philippos。あなたは本当にクサラナンダに彼がマンページを改善しなかったと言っていますか? 🙂
  • @ZsoltSzilagy私はそれを言ったわけでも、それを意味したわけでもありません。彼はあなたにとてもよく食べさせました、私はあなたが一人で食べるのに十分な年齢だと思います。 (-;

回答

この回答は、次の部分に分かれています。

  • -exec
  • -execsh -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のケース:

  1. 検索をさらに制限するためのテストとして。
  2. 見つかったファイルに対して何らかのアクションを実行するパス名(通常、ただし必ずしもそうとは限りませんが、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に配置されます。

  1. 文字列sh。これは、スクリプト内で$0として使用可能になり、内部シェルがエラーメッセージを出力すると、この文字列のプレフィックスが付けられます。

  2. 引数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とは、外部スクリプトの同じ名前の変数とは異なります。

上記は、とfindfind

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回、見つかったファイルごとにshmvの両方を実行します。大量のファイルの場合、著しく遅くなります。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です