Linuxでファイル名をまとめて変更する方法

f:id:monozukuri-bu:20170812232728j:plain

どーも、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)

この例の実用性は・・・? まあ、気にしない気にしない(;´∀`) \lPerl正規表現のテキスト修飾子で次の文字を小文字にするという機能です。気になる人は、「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

*1:yumでインストールできるのかは確認していません。後述するように正規表現の使えるrenameコマンドの実体はPerlスクリプトなので、どうしても使いたいなら、スクリプトコードを拾ってきて自分で/usr/local/bin/renameとかに保存しておくというのも可能なはずです。