zsh の zmv を使って簡単に複数ファイルを一括リネームする

連番のファイルがずらーっとあったとき、複数のファイル名を一気にスマートに変えたいことがある。一個ずつちまちまリネームなんてやってられない。そんなときは zsh の zmv を使うと便利なので紹介する。

zmv で何ができるか

例えばこんな感じで 1.txt から 6.txt までファイルがあったとする。

% ls
1.txt 3.txt 5.txt
2.txt 4.txt 6.txt

でも、ファイル名短すぎてわかりにくいなー、file-1.txt みたいに頭に file ってつけたいなー、って思ったとしよう。

そんなときのために zsh には zmv ってコマンドがあって、この手の一括リネームがスマートにできる。お手軽な使い方はこんな感じ。

あらかじめ ~/.zshrc にこう書いておいてから、

autoload -Uz zmv
alias zmv='noglob zmv -W'

一回のコマンドで複数ファイルをリネームできる。

% zmv *.txt file-*.txt

実行してみると ...

% ls
file-1.txt file-3.txt file-5.txt
file-2.txt file-4.txt file-6.txt

1.txt が file-1.txt, 2.txt が file-2.txt ... という風にリネームできた。便利。

使い方の解説

もうちょっと詳しく解説してみる。

オプションなしの使い方

さっきは alias でいろいろオプションつけてたんだけど、まずはオプションなしの場合で説明する。

% zmv '(*).txt' 'file-$1.txt'

これが一番基本的な使い方。左側(第一引数)のパターンにマッチしたファイルを右側(第二引数)のフォーマットで作った名前に mv できる。

カッコの指定と $1 ってのがポイントで、リネームするときに右側の $1 の部分は左側のカッコで囲んだ部分に置き換えられる。カッコの中にワイルドカードを入れておくと実際にマッチした部分が $1 として使われるので、元のファイル名の一部を使ってそれぞれの mv 先のファイル名が作れる。

例えば 005.txt ってファイルは 005 の部分が * にマッチするので $1 が 005 に置き換わって、結果として file-005.txt に mv される。

さらに $1 っていうぐらいなので、$2, $3 ... といくらでも続けれる。例えばこんなの。

% zmv 'hoge-(*).(*).(*).tar.gz' 'hoge-$1--$2--$3.tar.gz'

先に出てきたカッコから順に $1, $2, $3 と対応するので、この場合、hoge-1.2.3.tar.gz が hoge-1--2--3.tar.gz、hoge-2.10.8.tar.gz が hoge-2--10--8.tar.gz にリネームされる。

-w オプション

でもなんかカッコで囲むのめんどくさいな、って思った人もいるかもしれない。僕もそう思う。そんな人のために zmv には -w というオプションがある。

-w オプションをつけると左側のワイルドカード部分を勝手にカッコで囲んだものとして扱ってくれる。なので、さっきの tar.gz の例で言うと全部カッコを省略して書ける。

% zmv -w 'hoge-*.*.*.tar.gz' 'hoge-$1--$2--$3.tar.gz'
-W オプション

こうなってくると人間横着になるもので、右側の $1 ってのもめんどくさくなってくる。せっかく zmv を使うんだから極限まで楽をしたい。zmv を作った人もそう考えたのか、 -W というオプションを作ってくれた(大文字の W ね)。

-W オプションを使うと、 -w (小文字)に加えて右側の * も順番に $1, $2 ... と置き換えてくれる。なので最終的にはこれで OK。

% zmv -W 'hoge-*.*.*.tar.gz' 'hoge-*--*--*.tar.gz'

シンプル。

でも zmv '(*)-(*).txt' '$2-$1.txt' みたいに順番を入れ替えたい場合には使えない。そういうときは -W オプションなしでやろう。

noglob

さっきから引数を '...' で囲んでるけど、これは * をファイル名展開せずに zmv に渡すため。'...' で囲まなかった場合は zsh 自体が zmv に引数を渡す前に * を展開してしまうのでうまく動かない。zmv を使うときはこのファイル名展開は必要ないので無効にしておくといい。

% noglob zmv -W hoge-*.*.*.tar.gz hoge-*--*--*.tar.gz

こんな風に先頭に noglob を付けると zsh が * とかのワイルドカードを展開しなくなるので '...' で囲まなくてもよくなる。

便利な alias

最後の noglob を付けたパターンが典型的な使い方になるので、こんな風に ~/.zshrc に書いておくと便利。

autoload -Uz zmv
alias zmv='noglob zmv -W'

これで Windows のコマンドプロンプトみたいなのができるようになる。

% zmv *.txt *.c

進んだ使い方

ここまでが基本編。ここからもっと凝った使い方を紹介するので何かの時に使ってみよう。

ここから先はさっき紹介した alias とかの設定は無いものとして説明する。

確認する

-n オプションを使うと、実際にリネームせずに行おうとしてるコマンドの内容を出力してくれる。

% zmv -n -W '*.txt' '*.c'
mv -- file1.txt file1.c
mv -- file2.txt file2.c

難しいことやる前には一回これで確認しておくと安全。

-C, -L オプション

-C オプションを付けると mv の代わりに cp を実行するようになる。

# *.c ファイルを *.c.bak にコピーしてバックアップをとる
% zmv -C '(*.c)' '$1.bak'

-L オプションを付けると mv の代わりに ln を実行するようになる。

例えば、~/dotfiles ディレクトリの下に dot.zshrc, dot.vimrc とか dot.* という形式で設定ファイルを置いていたとしよう。ホームディレクトリに .zshrc とか本来の名前でリンクを作る場合はこう。

% cd  # ホームへ移動
% zmv -L -s 'dotfiles/dot.(*)' '.$1'

頭の dot を外してリンクが張れる。

% ls -al
.vimrc -> dotfiles/dot.vimrc
.zshrc -> dotfiles/dot.zshrc

-L を指定した場合はデフォルトでハードリンクが張られるんだけど、 -s を付けるとシンボリックリンクになる。普通は -s を付けておいた方がいいだろう。

あと、-M オプションを付けるとデフォルト動作に戻って mv コマンドを実行するようになる。

変数展開フラグ

右側のリネーム先ファイル名の部分で '${(U)1}.txt' みたいな感じで変数展開フラグが使える。といってもよく意味が分からないかもしれないので例を見てもらおう。

# *.txt の * 部分を大文字にしたい
% ls
readme.txt  todo.txt

# U フラグを使うと ...
% zmv '(*).txt' '${(U)1}.txt'

# カッコで囲んだところが大文字になった
% ls
README.txt  TODO.txt

# 小文字にしたいときは L フラグを使う
% zmv '(*).txt' '${(L)1}.txt'

# 戻った
% ls
readme.txt  todo.txt

変数展開フラグはいっぱいあるんだけど、便利なものは以下。

U
大文字にする(Upper)
L
小文字にする(Lower)
C
キャピタルケース。つまり readme を Readme にする

その他もっとマニアックなことがやりたい人は man zshexpn の Parameter Expansion Flags を参照。

変数名の置換

右側のリネーム先ファイル名の部分で、元の名前を置換できる。これも例を見た方がわかりやすい。

# なんかファイル名にスペースが入ってる。なんとかしたい
% ls -1
hoge 1 x.txt
hoge 2 y.txt

# ${1//pattern/repl} という形式で ...
% zmv '(*)' '${1// /_}'

# スペースが _ に置き換わった
% ls
hoge_1_x.txt  hoge_2_y.txt

ちなみに、スペースがいっぱいあるときは ## が便利。

# 誰だよ、こんなファイル作ったのは ...
% ls -1
hoge    1   x.txt
hoge   2     y.txt

# 直してやるか
% zmv '(*)' '${1// ##/_}'

% ls -1
hoge_1_x.txt
hoge_2_y.txt
# よしよし

## は1回以上の繰り返しを表すので、この場合は連続したスペースが1つの _ になる。

ディレクトリを降りて再帰的に変換する

zsher にはおなじみの **/* も使える。例えば今いるディレクトリ以下にある makefile を全部 Makefile にするのはこんな感じ。

% zmv '(**/)makefile' '$1Makefile'

今いるディレクトリ直下だけでなく、src/project1/makefile とか奥深くにあるファイルもリネームできる。

参考資料

その他参考になる資料一覧を挙げておく。さらに進んだ使い方をするときに読むといいだろう。

zmv の man

何はともあれ zsh の man を見るのがいい。zmv は man zshcontrib に書いてある(だいぶ下の方)。

zsh の man は Web でも読める。
man zshcontrib の OTHER FUNCTIONS

zsh-lovers

zsh-lovers をインストールすると man zsh-lovers で zsh の使い方、Tips とかが読めるようになる。その中に ZMV-Examples ってのがあって、zmv の例がいっぱい書いてある。

こいつも Web で読める。
ZSH-LOVERS(1) - ZMV-Examples

ソースコード

あと、zmv のソースコードの最初の方にコメントで詳しい説明が書いてあるので参考になる。僕の環境(Ubuntu 10.10)では /usr/share/zsh/functions/Misc/zmv にインストールされてた。

ソースは sourceforge にもある。
SourceForge - zsh/zsh/blob - Functions/Misc/zmv

その他の man

変数展開、グロブ展開なんかの zsh 自体の機能が分かってくると色々凝ったリネームができるようになる。参考になる man は以下。

man zshexpn の FILENAME GENERATION
*, x## とかファイル名生成全般についての説明
man zshexpn の Globbing Flags
(*).(#i)jpg の #i とかファイル名生成のフラグについての説明 (#i は大文字小文字を無視するフラグ)
man zshexpn の Parameter Expansion
${1// /_} など $1, $2 ... を置換する方法
man zshexpn の Parameter Expansion Flags
${(U)1} (大文字にする) など

色々やり過ぎると「結局手で一個ずつ直した方が早かった」なんてことになるかもしれないけど、便利なときもあるのでうまく使おう。