同一ファイルをリストアップするPerlScript

ファイルの整理は重要ですが、どんなに綺麗に分類していたとしても、内容が同一のファイルまでは整理から漏れてしまう事は往々にしてあります。
そんな時に役立つPerlScriptのサンプルです。
使い方を忘れてしまう事がままあったので、かなりヘルプに力が入っています・・・

use strict;
use utf8;
use Encode;
use Digest::SHA1 qw(sha1_hex);
use Cwd;

# 変数定義
	my $error = 0;
	my $ccode = '';
	my $dir = '';
	my $dir_mes = '';
	my $ext = '';
	my $ext_mes = '';
	my $sub = '';
	my $command = '';
	my $file = '';
	my $sha1 = '';
	my $act_file = '';
	my $diff_file = '';
	my $flg = 0;
	my $count = 0;
	my %listing = ();
	my @flist = ();

# 文字コード指定
	if($ARGV[0] eq 'utf8'){
		# UTF-8
		binmode STDOUT,':encoding(utf8)';
		binmode STDERR,':encoding(utf8)';
		binmode STDIN,':encoding(shiftjis)';
		$ccode = 'UTF-8';
	}elsif($ARGV[0] eq 'euc'){
		# EUC-JP
		binmode STDOUT,':encoding(euc-jp)';
		binmode STDERR,':encoding(euc-jp)';
		binmode STDIN,':encoding(shiftjis)';
		$ccode = 'EUC-JP';
	}elsif($ARGV[0] eq 'sjis'){
		# SHIFT-JIS
		binmode STDOUT,':encoding(shiftjis)';
		binmode STDERR,':encoding(shiftjis)';
		binmode STDIN,':encoding(shiftjis)';
		$ccode = 'SHIFT-JIS';
	}else{
		# 規定外の文字コード指定、SHIFT-JIS
		binmode STDOUT,':encoding(shiftjis)';
		binmode STDERR,':encoding(shiftjis)';
		binmode STDIN,':encoding(shiftjis)';
		$ccode = $ARGV[0].' => SHIFT-JIS';
	}

# ヘルプ
	if( ($ARGV[0] eq '') || ($ARGV[0] eq '/h') || ($ARGV[0] eq '/help') || ($ARGV[0] eq '/?') || ($ARGV[0] eq '-h') || ($ARGV[0] eq '-help') || ($ARGV[0] eq '-?') ){
		print STDOUT "**HELP\n";
		print STDOUT "Usage : cyofukuchk.pl [[/h][/help][/?][CHARCODE]] [DIR] [EXT] [SUBDIR]\n\n";
		print STDOUT "        動作確認環境 :\n";
		print STDOUT "         Windows XP / Server 2003\n";
		print STDOUT "         ActivePerl 5.8.8 build 817\n\n";

		print STDOUT "        概要 :\n";
		print STDOUT "         重複ファイルを見つけ、該当ファイルをリストアップします。\n";
		print STDOUT "         重複ファイルとみなされたファイルは、その後、比較対象外になります。\n";
		print STDOUT "         ファイルとディレクトリ名にUNICODEが含まれているとエラーとなります。\n\n";

		print STDOUT "        説明 :\n";
		print STDOUT "         ファイルのハッシュ値をSHA-1アルゴリズムで求めます。\n";
		print STDOUT "         このハッシュ値が同一のファイルを、重複ファイルとして検出します。\n\n";

		print STDOUT "        オプション解説 :\n";
		print STDOUT "         オプションの指定なし\n";
		print STDOUT "          ヘルプを表示します。\n\n";

		print STDOUT "         /h /help /?\n";
		print STDOUT "          ヘルプを表示します。\n";
		print STDOUT "          -h -help -?でもヘルプの表示が可能です。\n\n";

		print STDOUT "         [CHARCODE]\n";
		print STDOUT "          標準出力と標準エラーの文字コードを1つ指定します。\n";
		print STDOUT "          指定が規定外の場合、SHIFT-JISになります。\n";
		print STDOUT "           sjis : SHIFT-JISにします。\n";
		print STDOUT "           utf8 : UTF-8にします。\n";
		print STDOUT "           euc  : EUC-JPにします。\n\n";

		print STDOUT "         [DIR]\n";
		print STDOUT "          処理するディレクトリを1つ指定します。\n";
		print STDOUT "          何も指定しないとカレントディレクトリを対象とします。\n\n";

		print STDOUT "         [EXT]\n";
		print STDOUT "          処理する拡張子をワイルドカードで1つ以上指定します。\n";
		print STDOUT "          ファイル名の指定も可能です。\n";
		print STDOUT "          何も指定しないとすべてのファイルを対象とします。\n\n";

		print STDOUT "         [SUBDIR]\n";
		print STDOUT "          サブディレクトリを含めてチェックするか指定します。\n";
		print STDOUT "          何も指定しないとサブディレクトリを含めます。\n";
		print STDOUT "           サブディレクトリを含める   : yes\n";
		print STDOUT "           サブディレクトリを含めない : no\n\n";

		print STDOUT "        コマンド例 :\n";
		print STDOUT "         C:\\CHK_DATA以下の全てのファイル\n";
		print STDOUT "          cyofukuchk.pl sjis C:\\CHK_DATA *.*\n\n";

		print STDOUT "         複数の拡張子を指定する場合\n";
		print STDOUT "          cyofukuchk.pl sjis C:\\CHK_DATA *.gif;*.png;*.jpg\n\n";

		print STDOUT "         半角スペースを含むディレクトリとファイル名を指定する場合\n";
		print STDOUT "          cyofukuchk.pl sjis \"C:\\CHK DATA\" \"data *.txt\"\n";

		exit(1);
	}

# 引数受け取り + ヘッダー生成
	print STDOUT "---------------------\n";
	print STDOUT '--日付        : '.`date /t`;
	print STDOUT '--時間        : '.`time /t`;
	print STDOUT "--出力コード  : ".$ccode."\n";

	# ディレクトリ
	$dir = $ARGV[1];
	if($dir eq ''){
		$dir = Cwd::getcwd();
		$dir =~ s/\//\\/g;
		$dir_mes = ' (自動設定)';
	}
	print STDOUT "--ディレクトリ: ".$dir.$dir_mes."\n";

	# 拡張子、またはファイル
	$ext = $ARGV[2];
	if($ext eq ''){
		$ext = '*.*';
		$ext_mes = ' (自動設定)';
	}

	# サブディレクトリの再帰
	$sub = $ARGV[3];
	if($sub eq 'no'){
		$command = 'DIR /B /A-D';
		print STDOUT "--検索範囲    : 現在のディレクトリのみ\n";
	}else{
		$command = 'DIR /S /B /A-D';
		print STDOUT "--検索範囲    : サブディレクトリを含める\n";
	}

	# その他
	print STDOUT "--検索ファイル: ".$ext.$ext_mes."\n";
	print STDOUT "--DIRコマンド : ".$command." \"".$dir."\\".$ext."\"\n";
	print STDOUT "---------------------\n";

# index生成
	%listing = ();
	@flist = ();

	print STDOUT "--information : 対象ファイルを検索しています...\n";

	# ディレクトリの有無
	if(! -d Encode::encode('shiftjis',$dir)){
		print STDERR "** error      : 指定ディレクトリは存在しません。\n";
		print STDERR "**information : 処理は行われませんでした。\n";
		exit(1);
	}

	# DIRコマンド実行
	if(! open(com,$command." \"".$dir."\\".$ext."\"|")){
		print STDERR "** error      : DIR コマンド実行に失敗しました。\n";
		print STDERR '**              DIR /S /B /A-D '.$dir.'\\'.$ext."\n";
		print STDERR "**information : 処理は行われませんでした。\n";
		exit(1);
	}
	while(<com>){
		push(@flist,Encode::decode('shiftjis',$_));
	}
	close(com);

	# ファイルが1つ以下であるか
	if($#flist < 1){
		print STDERR "** error      : 指定ディレクトリ内にはファイルが1つ以下しか存在しません。\n";
		print STDERR "**information : 処理は行われませんでした。\n";
		exit(1);
	}

	print STDOUT "--information : 対象ファイルのハッシュを取得しています...\n";

	# ファイルのハッシュ値を取得
	foreach(@flist){
		$file = $_;

		# サブディレクトリを含めない場合、先頭にディレクトリを追加
		if($sub eq 'no'){
			$file = $dir."\\".$file;
		}

		$file =~ s/\n//g;
		if(! open(in,Encode::encode('shiftjis',$file))){
			print STDERR "** error      : [".$file."] が開けません。\n";
			print STDERR "**              ファイルの有無、既にファイルを開いていないか、\n";
			print STDERR "**              ファイル名にUNICODEが含まれていないか確認してください。\n";
			$error++;
		}else{
			binmode(in);
			$sha1 = sha1_hex(<in>);
			$listing{$file} = $sha1;
		}
		close(in);
	}

# チェック
	$count = 0;
	print STDOUT "--information : 重複ファイルのチェックを開始しました。\n";

	# ファイルのハッシュ値を比較
	foreach $act_file (keys %listing){
		$flg = 0;

		if($listing{$act_file} eq ''){ next; }

		foreach $diff_file (keys %listing){
			if($act_file eq $diff_file){
				if($flg == 0){
					$flg = 1;
				}else{
					# 自分がもう1つあった場合はエラー
					print STDERR "** error      : [".$act_file."]はファイル名が重複しています。\n";
					print STDERR "**              ファイル名またはディレクトリ名に上記文字を含むものの中に、\n";
					print STDERR "**              UNICODEが含まれている可能性があります。\n";
					$error++;
				}
			}
			if(($listing{$diff_file} ne "") && ($act_file ne $diff_file) && ($listing{$act_file} eq $listing{$diff_file})){
				print STDOUT $act_file." <=> ".$diff_file."\n";
				$listing{$diff_file} = '';
				$count++;
			}
		}
		$listing{$act_file} = '';
	}

# フッター生成
	print STDOUT "---------------------\n";
	if($error != 0){
		print STDOUT "**エラー件数  : ".$error." 件\n";
	}
	print STDOUT "--総処理件数  : ".sprintf("%d",$#flist+1)." 件\n";
	print STDOUT "--重複件数    : ".$count." 件\n";
	print STDOUT "---------------------\n";

	print STDOUT "--information : 処理が完了しました。\n";

	exit(0);