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


37

コマンド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

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

回答:


46

以下は、アプローチの直接修正です。

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は一度呼び出されます(または多数のファイルで何度か呼び出されます)が、すべてのファイルに対してではありません。

私は削除basenameそれはおそらく間違っているので、あなたに質問を:あなたは、移動するfoo/bar/file8にはfile8_renamed、ありませんfoo/bar/file8_renamed

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

  • 短縮findなしで追加xargs
  • セキュリティステッカーを追加

x役に立たない場合:find . -type f -name 'file*' -exec mv {} "{}_renamed" \; xargsバージョンは最初の例と同じ効率を持ちます/
コスタス

これxは、アスカーのアプローチを直接修正するためだけのものです。
トーマスエルカー

2
(1){}シェル(sh -c "…")コマンドで直接使用することは非常に危険です—常に引数として渡す必要があります。(2)すべてのバージョンが構成をfindサポートするわけではありません{}_renamed。(3)xargsファイルの名前を変更するのとは対照的に、ファイルを削除するのに役立つステートメントがわかりません。
G-Manは「Reinstate Monica」と言います

@ G-Man:との違いxargsmvvs. rmではなく、{}vs。withoutの使用です。前者は似てmv file1 file1_renamed; mv file2 file2_renamedいますが、後者は似ていrm file1 file2ます。
トーマスエルカー

1
そして、_renamedファイルを元の名前に戻したい場合、どうしますか?
mcmillab

17

最初の答えを試しそれを少しいじった後私はそれを使用してわずかに短く複雑ではないことがわかった-execdir

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

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


2
findそのサポートの実装-execdirおよび{}ない全体として、それはまた、最も安全です。あなたは追加することもできます-imv(といえ-T、あなたの場合はmv、それをサポート)
ステファンChazelas

1
@StéphaneChazelasの方が、mvプロンプトを頼る代わりに、またはそれに加えて、実行する前に実行されるコマンドを出力するコマンドをfind使用-okdirすることもできます(実装がサポートするかどうかによって異なります)。
マティス

それに言及する価値-depthは、ディレクトリ名にも触れる場合にも良いアイデアです。
Ciro Santilli新疆改造中心法轮功六四事件

なんてこったは、ちょうどそれが見つかった-execdir1つの非常に迷惑な欠点を持っていない、find場合何かをすることを拒否PATH任意の相対パスが含まれています... askubuntu.com/questions/621132/... find: The relative path XXX is included in the PATH environment
チロSantilli新疆改造中心法轮功六四事件

6

別のアプローチは、while readループオーバーfind出力を使用することです。これにより、変数として各ファイル名にアクセスできます。変数は、のオプションsh -cを使用して別のプロセスを生成する追加コストや潜在的なセキュリティの問題を心配することなく操作できます。find-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

3

最初の答えを拡張し./、ファイル名引数にパスプレフィックスが存在するため、これがファイル名に追加されないことに注意してください。

トーマス・エルカーの答えを修正すると、これはより一般的なアプローチだと思います

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

xargsオプション:

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

-I replace-strのすべての出現はreplace-str、から読み取られた引数に置き換えられstdinます。そのため、必要に応じて他の文字列に変更できます。


なぜのpringf "%f\0"代わりにprint0
slm

1
@simは、元の名前の前に付ける名前に変更するときに邪魔になるシェルメタキャラクターが含まれている場合にプレフィックス-print0を生成するためです。(例えば、リネームに、へなど)./PATTERN0 - foo.txt00 - foo.txt1 - bar.txt01 - bar.txt
DAS-G

2

私はと似た何かをすることができたforfindmv

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

これにより、すべてのconfig.ymlファイルが検索され、名前が変更されますconfig.yml.bak


これには、変数展開などを使用できるという利点があります。$ {i:r}。いい答えだ。
サラミ

ええ、ほとんどすべての式を入力してfor、一括操作を実行できます。
Varun Risbud
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.