web-dev-qa-db-ja.com

検索を使用して複数のファイルの名前を変更する方法

コマンドfindコマンドを使用して、複数のファイル(file1 ... filenをfile1_renamed ... filen_renamed)に名前変更したい:

find . -type f -name 'file*' -exec mv filename='{}' $(basename $filename)_renamed ';'

しかし、このエラーが発生します:

mv: cannot stat ‘filename=./file1’: No such file or directory

ファイル名がシェル変数として解釈されないため、これは機能しません。

41
user164014

以下はあなたのアプローチの直接的な修正です:

find . -type f -name 'file*' -exec sh -c 'x="{}"; mv "$x" "${x}_renamed"' \;

ただし、一致するファイルが多数ある場合、一致ごとに新しいシェル(mvを実行する)を開始するため、これは非常に負荷がかかります。また、ファイル名に変な文字が含まれていると、爆発します。より効率的で安全なアプローチは次のとおりです。

find . -type f -name 'file*' -print0 | xargs --null -I{} mv {} {}_renamed

また、奇妙な名前のファイルを操作できるという利点もあります。 findがサポートしている場合、これは

find . -type f -name 'file*' -exec mv {} {}_renamed \;

xargsバージョンは、{}を使用しない場合に便利です。

find .... -print0 | xargs --null rm

ここでrmは1回(または多くのファイルで数回)呼び出されますが、すべてのファイルに対して呼び出されるわけではありません。

basenameを削除しました。おそらく間違っているためです:foo/bar/file8ではなくfile8_renamedfoo/bar/file8_renamedに移動します。

編集(コメントで提案されているとおり):

  • findなしの短縮されたxargsを追加
  • セキュリティステッカーを追加しました
49
Thomas Erker

最初の答え を試して少しいじった後、-execdirを使用すると、少し短くて簡単な方法で実行できることがわかりました。

find . -type f -name 'file*' -execdir mv {} {}_renamed ';'

それはまたあなたが必要とするものを正確に行うべきであるように見えます。

17
Matijs

別のアプローチは、find出力に対してwhile readループを使用することです。これにより、findsh -cオプションを使用して個別の-execプロセスを生成することによる追加のコスト/潜在的なセキュリティ問題を心配する必要なく、操作可能な変数として各ファイル名にアクセスできます。

find . -type f -name 'file*' |
    while IFS= read file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done

また、使用しているシェルが-dオプションをサポートしてread区切り文字を指定している場合、次のようにして、奇妙な名前のファイル(たとえば改行付き)をサポートできます。

find . -type f -name 'file*' -print0 |
    while IFS= read -d '' file_name; do
        mv "$file_name" "${file_name##*\/}_renamed"
    done
8
John B

最初の答え を展開したいと思います。./パス接頭辞がファイル名引数に存在するため、これはファイル名に追加することはできません。

Thomas Erkerの回答を変更して、これはより一般的なアプローチだと思います

find . -name PATTERN -printf "%f\0" | xargs --null -I{} mv {} "prefix {} suffix"

xargsオプション:

--nullstdinを介して渡される各引数がnull文字(\0)で終わることを示します。このように、ファイル名にスペースを含めることができます。そうしないと、各Wordがmvコマンドの異なるパラメーターとして脅かされます。

-I replace-strreplace-strが出現するたびに、stdinから読み取られた引数に置き換えられます。したがって、必要に応じて他の文字列に変更することができます。

4
Sdlion

forfindmvでも同様のことができました。

for i in $(find . -name 'config.yml'); do mv $i $i.bak; done

これにより、すべてのconfig.ymlファイルが見つかり、それらの名前をconfig.yml.bakに変更します

2
Varun Risbud