本稿ではrenameコマンドが使えないLinux環境においてファイル名を一括置換するワンライナーの例を紹介する。
forループを利用する場合
forループの使用が許容される場合は
for file in *test1*; do mv -n "$file" "${file/test1/test2}"; done
foreach file (*test1*) ; mv -n "$file" `echo "$file" | sed 's/test1/test2/'` ; end
などとすればよい。拡張子が .txt のファイルのみリネームしたい場合は *test1* の部分を *test1*.txt と書けばよい。
このうち、bash系のワンライナーの方が処理は速い。bashのワンライナーは、ループの中で外部プロセスを生成することなく、組み込み機能だけで処理を行うためである。
forループを利用しない場合
何らかの理由でforループの使用が躊躇われる場合は以下のようにする。
ls * | xargs -n 1 | sed 'p;s/test1/test2/g' | xargs -n 2 mv
この一連のコマンドは以下のように機能している。
-
ls *でカレントディレクトリ内の全ファイルとディレクトリをリストする。 -
xargs -n 1でそれらを1行ずつsedに渡す。 -
sed 'p;s/test1/test2/g'で各ファイルについて変更前後の名前をそれぞれ出力する。1回目(pの部分)はそのまま、2回目(sで置換している部分)はtest1をtest2に置き換えたもの。 -
xargs -n 2 mvで2つの引数をまとめてmvコマンドに渡し、ファイル名を変更する。最初の引数は元のファイル名、2番目の引数は新しいファイル名となる。-nオプションは同名のファイルが既に存在する場合に上書きを禁止する。
例えば、ディレクトリに次のファイルがある場合を考える。
file_test1.txt
file_test2.txt
test1_document.txt
この場合、コマンドは次のように動作する。
① ls * で全ファイルリストを取得:
file_test1.txt
file_test2.txt
test1_document.txt
② xargs -n1 で1行ずつ処理:
file_test1.txt
file_test2.txt
test1_document.txt
③ sed 'p;s/test1/test2/g' で各行を2回出力:
file_test1.txt
file_test2.txt
file_test2.txt
file_test2.txt
test1_document.txt
test2_document.txt
④ xargs -n 2 mv で2つの引数を渡し、mv でファイル名を変更:
mv file_test1.txt file_test2.txt
mv test1_document.txt test2_document.txt
先程のコマンドにより、ワンラインでこれを一括実行できる。
拡張子が .txt のファイルのみリネームしたい場合は ls * の部分を ls *.txt と書けばよい。
findを利用するケース
以下のように find コマンドを利用することもできる。カレントディレクトリ配下の全ファイルについて rename の操作を一括で実行したい場合には便利である。
find . -type f -name '*test1*' | sed 'p;s/test1/test2/' | paste - - | xargs -n 2 mv -n
paste - - によって標準入力を2行ずつ結合して一行にまとめている。
なお -n が2回登場しているが、 xargs コマンドの -n オプションと mv コマンドの -n オプションは別物なので、この形式で正しい。
find . -type f -name '*test1*'は現在のディレクトリ以下で名前に test1 を含むファイルを検索する。そのため、カレントディレクトリ配下の該当するすべてのファイルにmvコマンドが適用されることに注意。
sh を呼び出しているのでやや回りくどいが、以下の方法でも同様の処理が実行される。
find . -type f -name '*test1*' | xargs -I {} sh -c 'mv -n "$1" "${1//test1/test2}"' _ {}
環境によっては mv や ls などに特殊なaliasが設定されている場合もあるため、事前に挙動を検証してから実行することが推奨される。