#!/usr/bin/perl use Switch; use Digest::MD5; use Cwd; @DIR1=""; @DIR2=""; $REPORT=""; $DESTROY=""; $USAGE="Usage: $0 [-h] [-destroy] [-r reportName] -c ControlDir -d Directory1 [ Directory2 Directory3 ... ]\n\t-c ControlDir\t\tSet the control dir (the directory to check against)\n\t-r ReportName\t\tCreate a report of duplicates in file ReportName\n\t-destroy\t\tDelete duplicates in NON-control directories.\n\t-h\t\t\tPrint this help message.\n"; #************************************************************ # This method creates an array containing the LS file we want #************************************************************ sub makeMD5Hash { my @directory=(); if ( $#_ == -1 ) { $directory[0]= $_[0]; } else { push @directory , @_; } my $currentDir = "./"; %returnVal=(); for( $lo=0; $lo < $#directory +1; $lo++ ) { print "Generating hash data for $directory[$lo]\n"; $ls_cmd = "ls -R $directory[$lo]"; open (LS_CMD, "$ls_cmd |") or die "Couldn't run \"$ls_cmd\"!\n"; while() { chomp; #if it's a directory it'll end in a : if ( $_ =~ /:$/ ) { $currentDir=$_; $currentDir=~ s/://g; } else { if ( -f "$currentDir/$_" ) { open(FILE, "$currentDir/$_") or die "Couldn't open \"$currentDir/$_\n"; binmode(FILE); $returnVal{"$currentDir/$_"}=Digest::MD5->new->addfile(*FILE)->hexdigest; close(FILE); } } } close (LS_CMD); } return %returnVal; } #********************************************* # Finds potential recursive subdirectory loops #********************************************* sub checkSubdir { my @DIRECTORIES = @_; my $CWD = getcwd; for ($i = 0; $i < $#DIRECTORIES+1; $i++) { for($j=$i+1; $j < $#DIRECTORIES+1; $j++) { #if we're not the same directory if( "$DIRECTORIES[$i]" ne "$DIRECTORIES[$j]" ) { $first = $CWD ."/". $DIRECTORIES[$i]; $second = $CWD ."/". $DIRECTORIES[$j]; if( $first =~ /[^\/]$/ ) { $first = $first . "/"; } if( $second =~ /[^\/]$/ ) { $second = $second . "/"; } if( index($first,$second,0) != -1 ) { print "Error: Subdirectories found!\n"; print "\t$first\n\t$second!\n"; exit 1; } if( index($second,$first,0) != -1 ){ print "Error: Subdirectories found!\n"; print "\t$second\n\t$first\n"; exit 1; } } } }} #************************** #Get command line arguments #************************** for ($i=0; $i < $#ARGV+1; $i++) { $arg= $ARGV[$i]; $arg=~ s/-//g; switch ($arg) { case "r" { if ( $i == $#ARGV ) { print "Error: -r expects an argument.\n"; exit 1; } $i++; $REPORT=$ARGV[$i]; } case "c" { if ( $i == $#ARGV ) { print "Error: -c expects an argument.\n"; exit 1; } $i++; $DIR1[0]= $ARGV[$i]; if ( ! -d $DIR1[0] ) { print "Error: $DIR1[0] is not a valid directory!\n"; exit 1; } } case "destroy" { $DESTROY=1; } else { print "$USAGE"; exit 1; } } } if ( "$DESTROY" ne "" ) { print "WARNING: This is potentially destructive. \n"; print "\tThis will remove all duplicates from non-control directories!\n\n"; print "Are you SURE you want to do this? (yes/no): "; $answer=; if( $answer =~ /^[yY][eE][sS]$/ ) { print "Destroy mode enabled!\n" } else { print "Turning off destroy mode!\n"; $DESTROY=""; } } print "Checking @DIR1\n"; # Remove the trailing / that the directories may have for ($i = 0; $i < $#DIR1+1; $i++) { if ( $DIR1[$i] =~ /\/$/ ) { $DIR1[$i] =~ /(.+)\/$/; $DIR1[$i] = $1; } } %md5Hash1 = makeMD5Hash(@DIR1); %md5Hash2 = %md5Hash1; print "\nFinding duplicates:\n\n"; #find our duplicates @keys1 = keys %md5Hash1; for ( $h1 = 0; $h1 < $#keys1 + 1; $h1++) { $foundAny=0; #delete the item we're on so we don't check it delete $md5Hash2{$keys1[$h1]}; @keys2 = keys %md5Hash2; for ($h2 = 0; $h2 < $#keys2+1; $h2++) { if ( "$md5Hash1{$keys1[$h1]}" eq "$md5Hash2{$keys2[$h2]}" ) { if ( $foundAny == 0 ) { $duplicates{$keys1[$h1]} .= "$keys2[$h2]"; $foundAny=1; } else { $duplicates{$keys1[$h1]} .= ":$keys2[$h2]"; } #remove the value so we don't get duplicates of duplicates of du.. delete $md5Hash2{$keys2[$h2]}; } } } @keys= keys %duplicates; if ( "$REPORT" ne "" ) { open(REPORT,">$REPORT") or die "Error: Couldn't open $REPORT for writing!"; } for( $i = 0; $i < $#keys+1; $i++) { print "Duplicates of $keys[$i]:\n"; if ( "$REPORT" ne "" ) { print REPORT "Duplicates of ",getcwd,"/$keys[$i]\n"; } @temp = split(/:/,$duplicates{$keys[$i]}); for( $j = 0; $j < $#temp+1; $j++) { if ( "$DESTROY" ne "" ) { print "\tDestroying $temp[$j]\n"; unlink $temp[$j]; if ( "$REPORT" ne "" ) { print REPORT "\t",getcwd,"/$temp[$j] Destroyed!\n"; } } else { print "\t$temp[$j]\n"; if ( "$REPORT" ne "" ) { print REPORT "\t",getcwd,"/$temp[$j]\n"; } } } print "\n"; if ( "$REPORT" ne "" ) { print REPORT "\n"; } } if ( "$REPORT" ne "" ) { close(REPORT); }