Linuxでファイル名をまとめて変更する方法
どーも、xyakenです。普段はRubyを書いています。私の端末に眠っていた自分用の技術メモ的なものを公開していきます。
似たようなファイル名のファイルが大量にあって、一括でファイル名を変更したいような場合があると思います。 例えば、
$ ls file-0001 file-0003 file-0005 file-0007 file-0009 file-0002 file-0004 file-0006 file-0008 file-0010
というような感じで、file-*
というファイルが大量にある状況で、file_*
という名前に変えたいような場合です。
forとmvとか組み合わせてやることも出来ますが、スクリプトを組まなければいけないし、バグを埋め込みかねません。
こういう時は、rename
コマンドを使います。
rename
コマンドには2種類あるようで、「正規表現で置換が行えるもの」と「置き換え前と置き換え後を引数で指定するもの」があります。
Ubuntu 16.04 LTSでは正規表現が使えるrename
なので、こちらの例を紹介します。
CentOS 6.8ではこのrename
はデフォルトでは使えません。*1
まず、ファイル名を変更するので取り返しが付かないことにならないように動作確認するためのオプションです。実際に実行しないでどうなるか表示させるには-n
オプション(-nono
オプション)を使います。
$ rename -n 's/file-/file_/' * rename(file-0001, file_0001) rename(file-0002, file_0002) rename(file-0003, file_0003) rename(file-0004, file_0004) rename(file-0005, file_0005) rename(file-0006, file_0006) rename(file-0007, file_0007) rename(file-0008, file_0008) rename(file-0009, file_0009) rename(file-0010, file_0010)
コレを実際に実行するには以下のようにします。
$ rename 's/file-/file_/' * $ ls file_0001 file_0003 file_0005 file_0007 file_0009 file_0002 file_0004 file_0006 file_0008 file_0010
rename
コマンドの書式は、以下のとおりです。
rename 'Perlの正規表現コマンド' 名前を変えるファイル
rename
コマンド自体は実はPerlスクリプトで引数で受け取ったコマンドを更にPerlに渡して実行しているようです。なので、おそらく、Perlの正規表現コマンドは全部使えるんじゃないかと思います。Perlはほとんど書いたことがないから、コードをぱっと見た感じの印象ですが(^.^;
いくつかの例を出します。
ファイル名の大文字を小文字にする例
$ ls HOGE $ rename 'y/A-Z/a-z/' * $ ls hoge
yコマンドは例えば、
y/src/des/
なら、sをdに、rをeに、cをsに置き換えるコマンドです。上記の通り、範囲指定も出来ます。
なお、sed
でyコマンドを使う時に、変換元と変換先の文字数が不一致だとエラーになりますが、rename
コマンドの場合、エラーにならず、足りない分はすべて変換先の最後の文字になるようです。
$ ls hoge $ rename 'y/egoh/123/' * $ ls 3321
元のファイル名にあった文字列を使い回す例(_の前後を入れ替える例)
$ rename -n 's/(.*)_(.*)/$2_$1/' * rename(file_0001, 0001_file) rename(file_0002, 0002_file) rename(file_0003, 0003_file) rename(file_0004, 0004_file) rename(file_0005, 0005_file) rename(file_0006, 0006_file) rename(file_0007, 0007_file) rename(file_0008, 0008_file) rename(file_0009, 0009_file) rename(file_0010, 0010_file)
複数の置換をする例
例えば、以下のようなファイルがあって、_を消してbazfile0001のようにしたい場合、
$ ls baz_file_0001 baz_file_0003 baz_file_0005 baz_file_0007 baz_file_0009 baz_file_0002 baz_file_0004 baz_file_0006 baz_file_0008 baz_file_0010
単に、’s/_//‘としてしまってはダメです。
$ rename -n 's/_//' * rename(baz_file_0001, bazfile_0001) rename(baz_file_0002, bazfile_0002) rename(baz_file_0003, bazfile_0003) rename(baz_file_0004, bazfile_0004) rename(baz_file_0005, bazfile_0005) rename(baz_file_0006, bazfile_0006) rename(baz_file_0007, bazfile_0007) rename(baz_file_0008, bazfile_0008) rename(baz_file_0009, bazfile_0009) rename(baz_file_0010, bazfile_0010)
まあ、sed
だのvim
での置換だのを使いこなしていればすぐ分かることですが、gオプションが未指定だと1回しか置換をしないので、s/_//g
を指定しないといけません。
$ rename -n 's/_//g' * rename(baz_file_0001, bazfile0001) rename(baz_file_0002, bazfile0002) rename(baz_file_0003, bazfile0003) rename(baz_file_0004, bazfile0004) rename(baz_file_0005, bazfile0005) rename(baz_file_0006, bazfile0006) rename(baz_file_0007, bazfile0007) rename(baz_file_0008, bazfile0008) rename(baz_file_0009, bazfile0009) rename(baz_file_0010, bazfile0010)
キャメルケースのファイルをスネークケースのファイル名に変える例
$ rename -n 's/(.[^A-Z]*)(.[^A-Z]*)(.[^A-Z]*)/\l$1_\l$2_\l$3/' AliceBobClice rename(AliceBobClice, alice_bob_clice)
この例の実用性は・・・? まあ、気にしない気にしない(;´∀`)
\l
はPerlの正規表現のテキスト修飾子で次の文字を小文字にするという機能です。気になる人は、「Perl 正規表現 テキスト修飾子」とかでググってもらえれば。
補足
なお、置き換え後のファイル名がダブった場合、エラーになって、そのファイルの置き換えはされません。
$ rename -n 's/0//g' * rename(baz_file_0001, baz_file_1) rename(baz_file_0002, baz_file_2) rename(baz_file_0003, baz_file_3) rename(baz_file_0004, baz_file_4) rename(baz_file_0005, baz_file_5) rename(baz_file_0006, baz_file_6) rename(baz_file_0007, baz_file_7) rename(baz_file_0008, baz_file_8) rename(baz_file_0009, baz_file_9) rename(baz_file_0010, baz_file_1) $ rename 's/0//g' * baz_file_0010 not renamed: baz_file_1 already exists $ ls baz_file_0010 baz_file_2 baz_file_4 baz_file_6 baz_file_8 baz_file_1 baz_file_3 baz_file_5 baz_file_7 baz_file_9
この通り、baz_file_0010が残ってしまいます。上書きしてしまってよい場合は、-f
オプション(-force
オプション)を使います。
$ rename -f 's/0//g' * $ ls baz_file_1 baz_file_3 baz_file_5 baz_file_7 baz_file_9 baz_file_2 baz_file_4 baz_file_6 baz_file_8
もし、名前を変更しようとしているのがディレクトリの場合でなおかつ、ディレクトリ内にファイルがある場合は、-f
オプションを付けてもエラーになって移動されません。
$ ls * baz_file_0001: hoge baz_file_0002: baz_file_0003: baz_file_0004: baz_file_0005: baz_file_0006: baz_file_0007: baz_file_0008: baz_file_0009: baz_file_0010: piyo $ rename -f 's/0//g' * Can't rename baz_file_0010 baz_file_1: Directory not empty $ ls baz_file_0010 baz_file_2 baz_file_4 baz_file_6 baz_file_8 baz_file_1 baz_file_3 baz_file_5 baz_file_7 baz_file_9