ファイル名置換に関する便利なUNIXテクニック例
最後に、コマンドやシェルの制御構造を利用したいくつかの例を紹介しておきます。業務に合わせて応用しながら使ってください。
また、誤ってファイルを上書きするのを防ぐには、mvコマンド/cpコマンドに「-i」「-b」オプションを付けて実行するか、「ファイルの一括作成」「シンボリックリンクの作成」で紹介したテクニックを利用して事前に検証環境を構築し、動作確認を行ってください。
サブディレクトリに渡るすべてのファイル名だけに拡張子「.bak」を付ける
$ find . -type f -print0 | while read -r -d '' file; do mv "$file" "$file.bak"; done
xargsコマンドを利用する場合は、次のように指定できます。
$ find . -type f -print0 | xargs -0 -I% mv % %.bak
findコマンドのアクションを利用する場合は、次のように指定できます。
$ find . -type f -exec mv '{}' '{}.bak' \;
同一階層内のすべてのディレクトリ・ファイル名から拡張子「.bak」を取り除く
$ find * -maxdepth 0 -name "*.bak" -print0 | while read -r -d '' file; do mv "$file" "${file%%.bak}"; done
シェルの変数展開「${パラメータ%%パターン}」を用いて、拡張子名「.bak」を取り除いています。
xargsコマンドを利用する場合は、次のように指定できます。
$ find * -maxdepth 0 -name "*.bak" -print0 | perl -pe 's/\.bak\0/\0/g' | xargs -0 -I% mv %.bak %
同一階層の複数のファイル名の拡張子「.text」を「.txt」に置き換える
$ for file in *.text; { mv "$file" "${file%%.text}.txt"; }
xargsコマンドを利用する場合は、次のように指定できます。
$ find * -maxdepth 0 -print0 | perl -pe 's/\.text\0/\0/g' | xargs -0 -I% mv %.text %.txt
FedoraやCygwinなどrenameコマンドが使える環境では、より高速にファイル名の置換が可能です(改行文字が含まれるファイル名は置換されません)。
$ rename .text .txt *.text
renameコマンドを使うと、「test.text.text」のような二重拡張子を持つファイル名の場合、「test.txt.text」のように最初の拡張子名が置換されてしまいます。
$ ls test.text.text $ rename .text .txt *.text $ ls test.txt.text
サブディレクトリに渡るすべてのファイルの拡張子「.text」を「.txt」に置き換える
$ find . -type f -name "*.text" -print0 | while read -r -d '' file; do mv "$file" "${file%%.text}.txt"; done
シェルの変数展開「${パラメータ%%パターン}」を用いて、拡張子名「.text」を取り除いています。
xargsコマンドを利用する場合は、次のように指定できます。
$ find . -type f -name "*.text" -print0 | perl -pe 's/\.text\0/\0/g' | xargs -0 -I% mv %.text %.txt
サブディレクトリに渡るすべてのファイルの拡張子を「.txt」に変更する
$ ls
test A.txt test".txt test.txt.bak
$ find . -type f -print0 | while read -r -d '' file; do mv "$file" "${file%.*}.txt"; done
$ ls
test A.txt test".txt test.text.txt
シェルの変数展開「${パラメータ%パターン}」を用いて、一度拡張子を取り除いてから、拡張子を付け足しています。「%%」ではなく「%」とすることで、二重拡張子のファイルでも、最後の拡張子のみを置換してくれます。
サブディレクトリに渡る拡張子のないすべてのファイルに「.txt」を付ける
$ find * -type f ! -name "*.*" -print0 | while read -r -d '' file; do mv "$file" "$file.txt"; done
xargsコマンドを利用する場合は、次のように指定できます。
$ find * -type f ! -name "*.*" -print0 | xargs -0 -I% mv % %.txt
findコマンドのアクションを利用する場合は、次のように指定できます。
$ find . -type f ! -name "*.*" -exec mv '{}' '{}.bak' \;
サブディレクトリに渡るすべてのディレクトリ・ファイル名に拡張子「.bak」を付ける
$ find * -print0 | sort -rz | while read -r -d '' file; do mv "$file" "$file.bak"; done
最初にディレクトリ名が置換されると、そのディレクトリ内のファイル名置換が正しく行われません。そのため、ディレクトリ名の置換は最後に行うよう、sortコマンドの「-r」オプションで検索結果を逆順に並べています。
sortコマンドの「-z」オプションは、「\0」(ヌル文字)を区切り文字とするために付けています。「find -print0」や「xargs -0」などと組み合わせて使う場合に必要なオプションです。
xargsコマンドを利用する場合は、次のように指定できます。
$ find * -print0 | sort -rz | xargs -0 -I% mv % %.bak
同一階層のすべてのファイル名の先頭に「日付-」を加える
$ ls
test A.txt test".txt test.text.bak
$ for file in *; { mv "$file" "$(date +%F)-$file"; }
$ ls
2009-02-09-test A.txt 2009-02-09-test".txt 2009-02-09-test.text.bak
dateコマンドの「+%F」オプションにより、「yyyy-mm-dd」形式の日時を出力しています。
xargsコマンドを利用する場合は、次のように指定できます。
$ find * -maxdepth 0 -type f -print0 | xargs -0 -I% mv % "$(date +%F)"-%
findコマンドのアクションを利用する場合は、次のように指定できます。
$ find * -maxdepth 0 -type f -exec mv '{}' "$(date +%F)"-'{}' \;
サブディレクトリに渡るすべてのファイル名の拡張子の前に「日付-」を加える
$ ls
test A.txt test".txt test.text.bak
$ find . -type f -print0 | while read -r -d '' file; do mv "$file" "${file%.*}-$(date +%F).${file##*.}"; done
$ ls
test A-2009-02-09.txt test"-2009-02-09.txt test.text-2009-02-09.bak
拡張子を取り除いて日付を付加し、最後に拡張子を加えています。ここでは、シェルの変数展開「${パラメータ##パターン}」を用いて、ファイル名の前方から「*.」にマッチする部分を除いています。
なお、コマンドが長い場合は、次のように改行した指定もできます。
$ find . -type f -print0 | while read -r -d '' file;
> do mv "$file" "${file%.*}-$(date +%F).${file##*.}";
> done
上記の「>」はコマンドがまだ完結していないときにシェルが表示する2次プロンプトです。
まとめ
今回は「ファイル・ディレクトリ名の一括置換」や「ファイルやシンボリックリンクの一括作成」に関するテクニックを紹介しました。
今回紹介した指定方法以外にも、コマンドやシェルの制御構造を利用することで、さまざまな指定ができると思います。ただし、特殊文字や空白が含まれるファイル名でも正しく動作するかを確認して業務で使うようにしてください。
次回以降も、テーマごとにテクニックや小技を紹介していく予定です。
