画像ファイルの整理(1)

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 を実行すれば、もとに戻せるという、親切設計になっています。

[前のぺ−ジ] [目次] [次のぺ−ジ]