Findでこれらのコマンドをどのように実行できますか?

Findでこれらのコマンドをどのように実行できますか?

FreeBSDを使用していますが、文字列の一部として{}を含むコマンドを実行できません。たとえば、見つかったファイルの名前を変更する場合です。これは状況の例です。質問自体は、「{}」構文の一般的な問題を解決することです。

find . -type f -name 'data*' -execdir mv {} OLD_{} \;
find . -type f -name 'data*' -execdir mv {} archive/{} \;

-execdirインクルードディレクトリ内でコマンドを実行して{}ファイル名のみを拡張すると、インクルードディレクトリパスに関する問題を回避できます。

2つの質問があります。

  1. -execdir mv節でパラメータを適切に引用する方法(多くのファイルの名前にはスペースまたは一重引用符があります)。
  2. ファイル名を完全に変更するようにターゲットを取得するにはどうすればよいですか?

2番目の問題は、{}「見つかったエントリのパス」が先行/末尾のスペースで囲まれている場合にのみ拡張されるように見えるため、コマンドパラメータがめちゃくちゃになるために発生します。空白以外の先行文字と末尾文字の例:

# /usr/bin/find . -maxdepth 1 -execdir echo "(result):" {}  \;
(result): .
(result): dir 1
(result): dir 2
(result): dir 3

# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{}  \;
(result):
(result):
(result):
(result):

# /usr/bin/find . -maxdepth 1 -execdir echo {}":(result)" \;
:(result)
:(result)
:(result)
:(result)

man find指摘-execおよび-ok rudimentaryの歴史的実装は、ユーティリティ名またはユーティリティ引数の前または後に空白以外の文字がある場合は文字列 "{}"を置き換えませんでした。ユーティリティ名のどこにあっても、このバージョンはを実行します。交換しないと、引数が表示されます。が、そんなことは起こらないようです。

実行したいコマンドをどのように実行しますか?

答え1

この動作は、シェルとして使用するとfindFreeBSD 11.1-RELEASEでは再現できません。/bin/sh以前は/bin/cshしかし、両方とも再現できました/bin/tcsh

cshとでこの問題を解決するには、を引用するか、tcsh次の方法を使用します。{}\{\}'{}'


文字列の一部として使用されたときにfind正しく拡張されない実装では、現在のパス名を他の文字列と正しく関連付けるには、サブシェルを使用して実行できます。{}

例:

find . -type f -name '*.c' -exec sh -c 'printf "(result):%s\n" "$@"' sh {} +

またはecho(ただし、「参照」)なぜprintfがechoより優れているのですか?"),

find . -type f -name '*.c' -exec sh -c '
    for name do
        echo "(result):$name"
    done' sh {} +

または

find . -type f -name '*.c' -execdir sh -c '
    for name do
        mv -- "$name" "OLD_${name##*/}"
    done' sh {} +

つまり、sh -cコマンドライン引数としてサブシェル(ここ)のパス名を指定し、通常はシェル変数を使用するのと同じように、結果シェルでそのパス名を使用して接続します。

${name##*/}上記は、GNUがパス名を使用するときにパス名の前に追加するのを防ぐためですfind)。./-execdir

関連:

答え2

最後の2つの例は混乱しています。ここでfindreplacementなしで{}そのまま維持されると考えると、出力は次のようになります(result):{}

# /usr/bin/find . -maxdepth 1 -execdir echo "(result):"{}  \;
(result):

たぶんシェルがこれらのfoo{}文字列を壊しているのでしょうか?

これが私がtcshすることです。

$ tcsh -c 'echo foo{}bar '
foobar
$ tcsh -c 'echo foo {} bar '
foo {} bar

これはまた言及引用された他の質問に対するコメントから{}GNU は特定のシェルに対して {} を見つけてブロックします。どのシェルですか?

回避策:以下を含む文字列の周りに引用符を入れてください{}

$ tcsh -c 'echo "foo{}bar" '
foo{}bar

(引用が必要な別のシェル{}ですが、fish個々のシェルも削除します{}。)

答え3

次のようなものを使用する方が良いでしょう。

find . -type f -name 'data*' -print0 | xargs -0 -I % mv '%' 'OLD_%'

find -print0 | xargs -0 を使用すると、各ファイル名が null バイトで区切られるため、ファイル名のスペースやその他の特殊文字のため、ファイル名は 2 つの個別の引数に分割されません。

関連情報