Linux でファイルのリストを見るには ls コマンドを使いますが、Perl では glob というファンクションがあります。
@files = glob ("*.gif");
とすれば、*.gif というパターンに一致するファイル名を配列 @files1 に格納してくれます。個々のファイル名を取りだすには
for $file (@files){
print $file, "\n";
}
とすればいいわけです。
異なったパターンをファイルを一括して得たい場合は
@files1 = glob ("*.gif");
@files2 = glob ("*.jpg");
@files3 = glob ("*.png");
@files = (@files1, @files2, @files3);
とすれば、HTML で使うことのできる画像ファイルを得ることができます。
これから作ろうとしているのは、JPG ファイルに連番をつけて整理するプログラムです。デジタルカメラで撮った JPG ファイルを「人物」「物体」「風景」などのカテゴリーに分け、それぞれに連番をつけて整理しようというものです。glob で得られるファイル名はファイル名順でソートされていますので、連番はファイル名順で付けられることになります。
新しいファイル名は A234.jpg B045.jpg などと、「ファイル名」+「三桁の数字」+「拡張子」 とすることにしました。ですから、新しいファイル名を得るには、fukei.jpg などとなっているファイル名から .jpg 以前の部分得る必要があります。これには
@body = split /\./, $oldname;という式を使います。正規表現ではピリオド(.)はそのままでは別の意味になりますので、\ でキャンセルして \. と表します。これによって .jpg 以前の部分は @body という配列の最初、つまり $body[0] に入り、jpg は $body[1] に入ることになります。次の部分では @files = glob ("*.jpg") で得たファイル名から、古いファイル名と新しいファイル名を一度に取りだしています。
# 新しいファイル名を @newname に格納
$sum = 0;
for $oldname (@files){
@body = split /\./, $oldname;
if($number < 10){
$p_number = "00$number";
}
elsif($number < 100){
$p_number = "0$number";
}
else{
$p_number = $number;
}
$newfiles[$sum] = "$newname$p_number.$body[1]";
$sum++;
$number++;
}
$number は、コマンドラインから初期値を得るようにするので、ここではわざと $number = 1 などと初期化していません。また、$number が 1 から 9 までは 001, 002, 003 など、$number が 10 から 99 までは 097, 098, 099 などと表示するようにするため、'0' をつけるようにしている部分もお見逃しなく。$sum は、ファイルが何個あったかを知るための変数で、ファイルの総計をカウントしてくれます。
これで system ("mv $oldname $newfiles[$count]"); としてやれば、ファイル名を変換してくれるのですが、$oldname と $newfiles[$count] が同じ名前の場合、エラーが出ますので、あらかじめ、$oldname と $newfiles[$count] を比較しておいて、もし、同じファイル名があったら、終了して別のファイル名なり、開始番号なりを入力してやりなおさせるようにしておきました。これが、次の部分です。
# オリジナルのファイル名と新しいファイル名の比較
# 同じものがあれば作業をしない
for $oldname (@files){
for $newname (@newfiles){
if($oldname eq $newname){
print "Found the same file name!\n";
exit(0);
}
}
}
以上を一つにまとめたのが次のスクリプトです。
#!/usr/bin/perl
# renames.pl
#
# 画像ファイルに連番をつけて名前を変更する
# 開始番号の取得
if($ARGV[1] eq ""){
$number = 1;
}
else{
$number = $ARGV[1];
}
# 画像ファイル名の取得
$newname = $ARGV[0];
if ($newname eq ""){
print "Input new file name [and start number].\n";
print "You can rename back by running <backnames>.\n";
exit (0);
}
# ファイル一覧の取得
# オリジナルのファイル名を @files に格納
@files = glob ("*.jpg");
# 新しいファイル名を @newname に格納
$sum = 0;
for $oldname (@files){
@body = split /\./, $oldname;
if($number < 10){
$p_number = "00$number";
}
elsif($number < 100){
$p_number = "0$number";
}
else{
$p_number = $number;
}
$newfiles[$sum] = "$newname$p_number.$body[1]";
$sum++;
$number++;
}
# オリジナルのファイル名と新しいファイル名の比較
# 同じものがあれば作業をしない
for $oldname (@files){
for $newname (@newfiles){
if($oldname eq $newname){
print "Found the same file name!\n";
exit(0);
}
}
}
# バックアップを backnames に保存する
open OUT, ">backnames";
print OUT "#!/bin/bash\n";
# ファイル名の変換
$count=0;
for $oldname (@files){
system ("mv $oldname $newfiles[$count]");
print OUT "mv $newfiles[$count] $oldname\n";
$count++;
}
close (OUT);
# バックアップファイルに実行属性をつけて逆変換を可能にする
system ("chmod +x backnames");
上記で、工夫したのは、
# バックアップを backnames に保存する
open OUT, ">backnames"; ....(1)
print OUT "#!/bin/bash\n"; ....(2)
# ファイル名の変換
$count=0;
for $oldname (@files){
system ("mv $oldname $newfiles[$count]");
print OUT "mv $newfiles[$count] $oldname\n"; ....(3)
$count++;
}
close (OUT);
# バックアップファイルに実行属性をつけて逆変換を可能にする
system ("chmod +x backnames"); ....(4)
の部分です。backnames というファイルを書き込みモードでオープンして(1)、これをシェルスクリプトにします(2)。逆変換ができるようにコマンドを書き込み(3)、最後にパーミッションを「実行可」にしておきます。こうすれば、「あっ、変換するんじゃなかった」と悲しい思いをすることなく、./backnames を実行すれば、もとに戻せるという、親切設計になっています。