もた日記

くだらないことを真面目にやる

ZSH-LOVERSメモ(7) : ZMVを使った一括リネームのパターン

ZSH-LOVERS(1)というZshのTipsを紹介しているページがあるので見てみる(続き)。
7番目はZMVコマンドで複数ファイルのリネームが簡単にできる。詳細についてはman zshcontribに記述されている。
ZMVコマンドを使うためにはautoloadが必要。

autoload -U zmv

簡単な使い方はzmvとコマンドを打ってリターンすると確認できる。

$ zmv
Usage:
  zmv [OPTIONS] oldpattern newpattern
where oldpattern contains parenthesis surrounding patterns which will
be replaced in turn by $1, $2, ... in newpattern.  For example,
  zmv '(*).lis' '$1.txt'
renames 'foo.lis' to 'foo.txt', 'my.old.stuff.lis' to 'my.old.stuff.txt',
and so on.  Something simpler (for basic commands) is the -W option:
  zmv -W '*.lis' '*.txt'
This does the same thing as the first command, but with automatic conversion
of the wildcards into the appropriate syntax.  If you combine this with
noglob, you don't even need to quote the arguments.  For example,
  alias mmv='noglob zmv -W'
  mmv *.c.orig orig/*.c

直感的に使うにはnoglob zmv -Wとすればよい。
いきなり実行するのは怖いのでその場合は-nオプションを指定すれば、実際には実行せずに対象となるファイルを確認できる。

$ noglob zmv -W -n *.txt *.csv                                                                                                 ⏎
mv -- a.txt a.csv
mv -- b.txt b.csv



簡単な使い方はわかったのでZSH-LOVERSの内容を見てみる。ちゃんと理解できるまでは-nオプションを付けで事前に確認した方がよさそう。

# Remove illegal characters in a fat32 file system. Illegal characters are
#   / :  ;  *  ?  "  <  >  |
# NOTE: ``-Q'' and (D) is to include hidden files.
  $ unwanted='[:;*?\"<>|]'
  $ zmv -Q "(**/)(*$~unwanted*)(D)" '$1${2//$~unwanted/}'

上記の例はunwantedで指定した記号を取り除く。

# Changing part of a filename (i. e. "file-hell.name" -> "file-heaven.name")
  $ zmv '(*)hell(*)' '${1}heaven${2}'
  # or
  $ zmv '*' '$f:s/hell/heaven/'

ファイル名の一部を置換。:sは編集子。

# remove round bracket within filenames
# i. e. foo-(bar).avi -> foo-bar.avi
  $ zmv '*' '${f//[()]/}'

丸カッコを取り除く。

# serially all files (foo.foo > 1.foo, fnord.foo > 2.foo, ..)
  $ autoload zmv
  $ ls *
  1.c  asd.foo  bla.foo  fnord.foo  foo.fnord  foo.foo
  $ c=1 zmv '*.foo' '$((c++)).foo'
  $ ls *
  1.c  1.foo  2.foo  3.foo  4.foo  foo.fnord

連番でリネーム。

# Rename "file.with.many.dots.txt" by substituting dots (exept for the last
# one!) with a space
  $ touch {1..20}-file.with.many.dots.txt
  $ zmv '(*.*)(.*)' '${1//./ }$2'

拡張子が連続しているファイルの一番最後の拡張子のみ残してリネーム。上記の例の場合、1-file.with.many.dots.txt1-file with many dots.txtになる。

# Remove the first 4 chars from a filename
  $ zmv -n '*' '$f[5,-1]' # NOTE: The "5" is NOT a mistake in writing!

最初の4文字を取り除く。

# Rename names of all files under the current Dir to lower case, but keep
# dirnames as-is.
  $ zmv -Qv '(**/)(*)(.D)' '$1${(L)2}'

カレントディレクトリ以下のファイル名を小文字にする。ディレクトリ名はそのまま。

# replace all 4th character, which is "1",  with "2" and so on
  $ autoload -U zmv
  $ zmv '(???)1(???[1-4].txt)' '${1}2${2}'

4番目の文字を変更したい場合など。上記の例の場合00010001.txt00020001.txtになる。

# Remove the first 15 characters from a string
  $ touch 111111111111111{a-z}
  $ autoload zmv
  $ zmv '*' '$f[16,-1]'

最初の15文字を取り除く(4文字を取り除く例と同じ)。

# Replace spaces (any number of them) with a single dash in file names
  $ autload zmv
  $ zmv -n '(**/)(* *)' '$1${2//( #-## #| ##)/-}'
  # or - with Bash
  $ find . -depth -name '* *' -exec bash -c '
  > shopt -s extglob
  > file=$1
  > dir=${file%/*}
  > name=${file##*/}
  > newname=${name//*([ -]) *([ -])/-}
  > mv -i -- "$file" "$Dir/$newname"' {} {} \;

空白を-に置換。

# Clean up file names and remove special characters
  $ autoload zmv
  $ zmv -n '(**/)(*)' '$1${2//[^A-Za-z0-9._]/_}'

A-Za-z0-9._以外の文字を_に置換。

# Add *.py to a bunch of python scripts in a directory (some of them end
# in *.py and give them all a proper extension
  $ autoload zmv
  $ zmv -n '(**/)(con*)(#qe,file $REPLY | grep "python script",)' '$1$2.py'

特定ディレクトリ内(con*ディレクトリ以下のpython scriptディレクトリ内?)のファイルに.pyを追加。

# lowercase all extensions (i. e. *.JPG) incl. subfolders
  $ autoload zmv
  $ zmv '(**/)(*).(#i)jpg' '$1$2.jpg'
  # Or - without Zsh
  $ find Dir -name '*.[jJ][pP][gG]' -print | while read f
  > do
  >      case $f in
  >       *.jpg) ;
  >       *) mv "$f" "${f%.*}.jpg" ;
  >       esac
  > done

大文字の拡張子を小文字にする。上記の例は*.JPG*.jpgにする。

# remove leading zeros from file extension
  $ autoload zmv
  $ ls
  filename.001  filename.003  filename.005  filename.007  filename.009
  filename.002  filename.004  filename.006  filename.008  filename.010
  $ zmv '(filename.)0##(?*)' '$1$2'
  $ ls
  filename.1  filename.10  filename.2  filename.3  filename.4  filename.5 ..

ファイル名のゼロを取り除く。

# renumber files.
  $ autoload zmv
  $ ls *
  foo_10.jpg  foo_2.jpg  foo_3.jpg  foo_4.jpg  foo_5.jpg  foo_6.jpg ..
  $ zmv -fQ 'foo_(<0->).jpg(.nOn)' 'foo_$(($1 + 1)).jpg'
  $ ls *
  foo_10.jpg  foo_11.jpg  foo_3.jpg  foo_4.jpg  foo_5.jpg  ...

ファイル名に数値を加算。

# adding leading zeros to a filename (1.jpg -> 001.jpg, ..
  $ autoload zmv
  $ zmv '(<1->).jpg' '${(l:3::0:)1}.jpg'

ゼロ埋めでリネーム。<1->は1以上という表現。<1-10>とすれば1から10の間。

# See above, but now only files with a filename >= 30 chars
  $ autoload zmv
  $ c=1 zmv "${(l:30-4::?:)}*.foo" '$((c++)).foo'

ファイル名が30文字以上のファイルを1.foo2.fooのようにリネーム。

# Replace spaces in filenames with a underline
  $ autoload zmv
  $ zmv '* *' '$f:gs/ /_'

空白を_に置換。:gsは編集子(Modifiers)。

# Change the suffix from *.sh to *.pl
  $ autoload zmv
  $ zmv -W '*.sh' '*.pl'

拡張子を変更する。

# Add a "".txt" extension to all the files within ${HOME}
  # ``-.'' is to only rename regular files or symlinks to regular files,
  # ``D'' is to also rename hidden files (dotfiles))
  $ autoload zmv
  $ zmv -Q '/home/**/*(D-.)' '$f.txt'
  # Or to only rename files that don't have an extension:
  $ zmv -Q '/home/**/^?*.*(D-.)' '$f.txt'

${HOME}以下のファイルに.txtを追加。
最後のコマンドは拡張子がないファイルのみを対象。

# Recursively change filenames with characters ? [ ] / = + < > ; : " , - *
  $ autoload zmv
  $ chars='[][?=+<>;",*-]'
  $ zmv '(**/)(*)' '$1${2//$~chars/%}'

上記の例はcharsで指定した記号を%に置換。

# Removing single quote from filenames (recursively)
  $ autoload zmv
  $ zmv -Q "(**/)(*'*)(D)" "\$1\${2//'/}"

シングルクオートを取り除く。

# When a new file arrives (named file.txt) rename all files in order to
# get (e. g. file119.txt becomes file120.txt, file118.txt becomes
# file119.txt and so on ending with file.txt becoming file1.txt
  $ autoload zmv
  $ zmv -fQ 'file([0-9]##).txt(On)' 'file$(($1 + 1)).txt'

file119.txtfile120.txtのようにファイル名に数値を加算してリネームしたい場合。-fはforceオプションなのでファイルが存在すれば上書きされる。

# lowercase/uppercase all files/directories
  $ autoload zmv
  $ zmv '(*)' '${(L)1}' # lowercase
  $ zmv '(*)' '${(U)1}' # uppercase

小文字化。大文字化。(L)(U)はParameter Expansion Flagsというもの。

# Remove the suffix *.c from all C-Files
  $ autoload zmv
  $ zmv '(*).c' '$1'

拡張子を取り除く。

# Uppercase only the first letter of all *.mp3 - files
  $ autoload zmv
  $ zmv '([a-z])(*).mp3' '${(C)1}$2.mp3'

1文字目のみ大文字にする。(C)はParameter Expansion Flagsというものでキャピタライズできる。

# Copy the target `README' in same directory as each `Makefile'
  $ autoload zmv
  $ zmv -C '(**/)Makefile' '${1}README'

-Cを指定するとmvではなくcpができる。上記の場合はMakefileがあるディレクトリにREADMEというファイル名でコピーされる。

# Removing single quote from filenames (recursively)
  $ autoload zmv
  $ zmv -Q "(**/)(*'*)(D)" "\$1\${2//'/}"

シングルクオートを取り除く(2回目...)。

# Rename pic1.jpg, pic2.jpg, .. to pic0001.jpg, pic0002.jpg, ..
  $ autoload zmv
  $ zmv 'pic(*).jpg' 'pic${(l:4::0:)1}.jpg'
  $ zmv '(**/)pic(*).jpg' '$1/pic${(l:4::0:)2}.jpg' # recursively

ゼロ埋めでリネーム。