Perl 小品集 ─ back2zip

#!/usr/bin/perl
# back2zip.pl
# コピーしたいデイレクトリから実行する
# オプション
#  -b ZIP にディレクトリごとバックアップする
#  -r ZIP からローカルに復元する
#  -d ディレクトリの任意指定
# ファイルのタイムスタンプをチェックして新しいファイルだけをコピー
# 第2ステップで、オリジナルディレクトリで削除されたファイルをバッ
# クアップディレクトリでも削除する

# カレントディレクトリーを得る
($original_dir) = `pwd`;
chomp $original_dir;
# ZIP 上のディレクトリーを得る
$backup_dir = $original_dir;
$original_dir =~ /^(\/[^\/]+\/[^\/]+)/;
$backup_dir =~ s/$1/\/mnt\/zip/;

# ディレクトリの指定
($input) = @ARGV;
if ($input eq "-b" or $input eq "") {
    print "===== Backup to ZIP =====\n";
    print "Original: $original_dir\n";
    print "Backup:   $backup_dir\n";
    }
elsif ($input eq "-r") {
    print "===== Restore from ZIP =====\n";
    $temp = $original_dir;
    $original_dir = $backup_dir;
    $backup_dir = $temp;
    print "Original: $original_dir\n";
    print "Backup:   $backup_dir\n";
    }
elsif ($input eq "-d") {
    print "===== Input Directories  =====\n";
    print "Original: ";
    $original_dir = <STDIN>;
    chomp $original_dir;
    print "Backup:   ";
    $backup_dir = <STDIN>;
    chomp $backup_dir;
    }
else {
    print "back2zip - a backup utility\n";
    print " options:\n";
    print "  -b  Backup to ZIP\n";
    print "  -r  Restore from ZIP\n";
    print "  -d  Input directories\n";
    print "  -h  Show help\n";
    exit 0;
    }

# 開始の確認
# 親ディレクトリを子ディレクトリにコピーしようとすると、無限ループ
# になるので、これを禁止する
if ($backup_dir =~ /$original_dir/) {
    print "This action is prohibited.\n";
    exit 0;
    }

# エラーの保存
open OUT, ">$backup_dir/back2zip_error.txt";

print "\n***** Copy files from original to backup directory.\n";
print "***** Is this okay? [y/n] ";
$input = <STDIN>;
chomp $input;
if ($input eq 'y') {
    &backup;
    }
print "\n***** Remove unmatched files from backup directory.\n";
print "***** Is this okay? [y/n] ";
$input = <STDIN>;
chomp $input;
if ($input eq 'y') {
    &synch;
    }
exit 0;

sub backup {
# コピー先にディレクトリがなければ作る
if (!(-e "$backup_dir")) {
    if ((system "mkdir '$backup_dir'") == 0) {
        return;
        }
    # 失敗したら終了する
    else {
        print "Cannot make the directory.\n";
        exit 0;
        }
    }
# バックアップしたファイルのカウンター
$sum = 0;
# ディレクトリーのツリーをたどる
&backup_2 ($original_dir);
# 結果の報告
print "$sum files are updated.\n";
}

sub backup_2 {
local (@list, $work_dir, $original_file, $backup_file);
local ($work_dir) = @_;
# ディレクトリーのリストを得る
@list = glob "$work_dir/*";
foreach $original_file (@list) {
    # コピー先のファイ名を得る
    $backup_file = $original_file;
    $backup_file =~ s/$original_dir/$backup_dir/;
    # リストからディレクトリとファイルをわける
    # ディレクトリの場合
    if (-d "$original_file") {
        # ディレクトリがなければ作る
        if (!(-e "$backup_file")) {
            if ((system "mkdir '$backup_file'") != 0) {
                &error_log ("Cannot make $backup_file");
                }
            }
        # ディレクトリの中のファイルをたどる
        &backup_2 ($original_file);
        }
    # ファイルの場合
    else {
        # ファイルが無ければコピーする
        if (!(-e "$backup_file")) {
            if ((system "cp -p '$original_file' '$backup_file'") != 0 ) {
                &error_log ("Cannot copy $original_file");
                }
            else {
                print "$backup_file ... copied\n";
                $sum++;
                }
            }
        # ファイルがあればタイムスタンプを比較する
        else {
            # もとのファイルが新しければコピーする
            if ((&compare_time ("$original_file", "$backup_file")) > 0){
                if ((system "cp -p '$original_file' '$backup_file'") != 0) {
                    &error_log ("Cannot copy $original_file");
                    }
                else {
                    print "$backup_file ... updated\n";
                    $sum++;
                    }
                }
            }
        }
    }
}

sub synch {
# 削除するファイルのカウンター
$sum = 0;
# ディレクトリーのツリーをたどる
&synch_2 ($backup_dir);
# 結果の報告
print "$sum directories/files were deleted.\n";
}

sub synch_2 {
local (@list, $original_file, $backup_file);
local ($work_dir) = @_;
# ディレクトリーのリストを得る
@list = glob "$work_dir/*";
foreach $backup_file (@list) {
    # コピー元のファイ名を得る
    $original_file = $backup_file;
    $original_file =~ s/$backup_dir/$original_dir/;
    # ディレクトリの場合
    if (-d "$backup_file") {
        # ディレクトリの中のファイルをたどる
        &synch_2 ($backup_file);
        # ディレクトリが無い時
        if (!(-e "$original_file")) {
            if ((system "rmdir '$backup_file'") != 0) {
                &error_log ("Cannot remove $backup_file");
                }
            else {
                print "$backup_file/ ... removed\n";
                $sum++;
                }
            }
        }
    # ファイルの場合
    else {
        # ファイルが無い時
        if (!(-e "$original_file") and !($backup_file =~ /back2zip_error.txt$/)) {
            if ((system "rm '$backup_file'") != 0) {
                &error_log ("Cannot remove $backup_file");
                }
            else {
                print "$backup_file ... removed\n";
                $sum++;
                }
            }
        }
    }
}

# タイムスタンプの比較
sub compare_time {
local ($original_file, $backup_file) = @_;
# それぞれのファイルの更新時刻を得る
local ($original_time) = &get_mtime ("$original_file");
local ($backup_time) = &get_mtime ("$backup_file");

# もとのファイルが新しい場合
if ($original_time > $backup_time) {
    return 1;
    }
# そうでない場合
else {
    return 0;
    }
}

# タイムスタンプを得る
sub get_mtime {
local ($file) = @_;
# ファイル情報を得る
local ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat "$file";
# 更新時刻を返す
return $mtime;
}

sub error_log {
local ($message) = @_;
print OUT "$message\n";
}


[INDEX]