web-dev-qa-db-ja.com

find -exec mv {} ./target/ +が機能しないのはなぜですか?

{} \;{} \+| xargs ...の機能を正確に知りたい。説明でこれらを明確にしてください。

以下の3つのコマンドが実行され、同じ結果が出力されますが、最初のコマンドには少し時間がかかり、形式も少し異なります。

find . -type f -exec file {} \;
find . -type f -exec file {} \+
find . -type f | xargs file

これは、最初のコマンドがfileコマンドからのすべてのファイルに対してfindコマンドを実行するためです。したがって、基本的には次のように実行されます。

file file1.txt
file file2.txt

ただし、後者の2は-execコマンドで検索し、以下のようなすべてのファイルに対してfileコマンドを1回実行します。

file file1.txt file2.txt

次に、次のコマンドを実行します。最初のコマンドは問題なく実行されますが、2番目のコマンドではエラーメッセージが表示されます。

find . -type f -iname '*.cpp' -exec mv {} ./test/ \;
find . -type f -iname '*.cpp' -exec mv {} ./test/ \+ #gives error:find: missing argument to `-exec'

{} \+を含むコマンドの場合、エラーメッセージが表示されます

find: missing argument to `-exec'

何故ですか?誰かが私が間違っていることを説明できますか?

96

マニュアルページ (または オンラインGNUマニュアル )で、ほとんどすべてが説明されています。

find -execコマンド{} \;

結果ごとに、command {}が実行されます。 {}のすべての出現は、ファイル名に置き換えられます。 ;の先頭にはスラッシュが付き、シェルが解釈できないようにします。

find -execコマンド{} +

各結果はcommandに追加され、その後実行されます。コマンドの長さの制限を考慮に入れると、このコマンドがより多く実行される可能性があると思います。

コマンドの呼び出しの総数は、一致するファイルの数よりもはるかに少なくなります。

マニュアルページのこの引用に注意してください。

コマンドラインは、xargsがコマンドラインを作成するのとほぼ同じ方法で作成されます

空白を除いて、{}+の間に文字が許可されないのはそのためです。 +は、xargsと同様にコマンドに引数を追加する必要があることをfindに検出させます。

ソリューション

幸いなことに、GNUのmv実装は、-tまたはより長いパラメーター--targetのいずれかで、ターゲットディレクトリを引数として受け入れることができます。使用方法は次のとおりです。

mv -t target file1 file2 ...

findコマンドは次のようになります。

find . -type f -iname '*.cpp' -exec mv -t ./test/ {} \+

マニュアルページから:

-execコマンド;

コマンドを実行します。 0ステータスが返された場合はtrue。 findに続くすべての引数は、 `; 'で構成される引数までコマンドの引数とみなされます遭遇します。文字列 `{} 'は、findの一部のバージョンのように、単独の引数だけでなく、コマンドの引数で発生するすべての場所で処理される現在のファイル名に置き換えられます。これらの構造は両方とも、シェルによる展開から保護するために、エスケープする必要があります(「\」で)。 -execオプションの使用例については、使用例セクションを参照してください。指定されたコマンドは、一致したファイルごとに1回実行されます。コマンドは開始ディレクトリで実行されます。 -execアクションの使用を取り巻く避けられないセキュリティ問題があります。代わりに-execdirオプションを使用する必要があります。

-execコマンド{} +

この-execアクションのバリアントは、選択されたファイルに対して指定されたコマンドを実行しますが、コマンドラインは、選択された各ファイル名を最後に追加することによって構築されます。コマンドの呼び出しの総数は、一致するファイルの数よりもはるかに少なくなります。コマンドラインは、xargsがコマンドラインを作成するのとほぼ同じ方法で作成されます。コマンド内では、 `{} 'の1つのインスタンスのみが許可されます。コマンドは開始ディレクトリで実行されます。

182
Lekensteyn

Mac OSXZSHシェルを使用して同じ問題が発生しました。この場合、mvには-tオプションがありません。別の解決策を見つけなければなりませんでした。ただし、次のコマンドは成功しました。

find .* * -maxdepth 0 -not -path '.git' -not -path '.backup' -exec mv '{}' .backup \;

秘密は中括弧を引用するでした。 execコマンドの最後に中括弧を置く必要はありません。

buntu 14.04BASHおよびZSH shells)でテストしましたが、同じように動作します。

ただし、+記号を使用する場合は、execコマンドの最後に配置する必要があるようです。

5
arvymetal

find -iname ... -exec mv -t dest {} +をサポートしないfind実装または-inameをサポートしないmv実装の-tと標準的に同等なのは、シェルを使用して引数の順序を変更することです。

find . -name '*.[cC][pP][pP]' -type f -exec sh -c '
  exec mv "$@" /dest/dir/' sh {} +

-name '*.[cC][pP][pP]'を使用することで、現在のロケールに依存して、cまたはpの大文字バージョンを決定することも避けます。

+とは対照的に、;はシェルでは特別ではないため、引用符で囲む必要はありません(引用演算子として\をサポートしていないrcのようなシェルを除き、引用は問題ありません)。

/の末尾の/dest/dir/は、1つのmvファイルのみが見つかり、foo.cppが存在しないか、ディレクトリ(またはディレクトリへのシンボリックリンク)でなかった場合に/dest/dir/dest/dirに名前変更する代わりに、エラーでcppが失敗するようにします。

3