#!/usr/bin/perl -w # Created by John Saathoff # Modified 30 July 2010 # www.jrsaathoff.com use Getopt::Long; use Cwd 'realpath'; use File::Spec; use Switch; use Getopt::Long; use strict; use warnings; my ($opt_d, $opt_h, $opt_l, $opt_r) = (3,0,0,0); my ($opt_n, $opt_u, $opt_t, $opt_w) = (0,0,0,0,0); my ($opt_i, $opt_padnumbers, $opt_follow) = (0, 0, 0); my ($opt_examples) = (0); my (@opt_p, @opt_e, @opt_s) = ((),(),()); my $cols = 0; my %list = (); # Process command line options my $result = GetOptions ("d|digits=i" => \$opt_d, "e|patterne=s{2}" => \@opt_e, "h|?|help" => \$opt_h, "i|index=i" => \$opt_i, "n|name=s" => \$opt_n, "l|lcase" => \$opt_l, "p|pattern=s" => \@opt_p, "r|replace=s" => \$opt_r, "s|string=s" => \@opt_s, "t|test" => \$opt_t, "u|ucase" => \$opt_u, "w|whitespace" => \$opt_w, "follow-links" => \$opt_follow, "examples" => \$opt_examples, "pad-numbers" => \$opt_padnumbers); if (! $result) { die "\nIncorrect input, run -h for help\n"; } if ($opt_h) { usage(); } if ($opt_examples) { examples(); } # create a hash for each argument on the command line # the hash key is the absolute path to the file and the # hash value is the new name of the file for (my $i=0; $i < @ARGV; $i++) { my $path = File::Spec->rel2abs($ARGV[$i]); # Don't process directories if (! (-d $path)) { # the realpath command resolves system links, the removeUplinks is somethinging I wrote # that only removes .. from the directory path $path = ($opt_follow) ? realpath($path) : removeUplinks($path); $list{$path} = renameFile($path); } } #foreach my $key (sort keys %list) { # print "I: $key\n"; # print "O: $list{$key}\n"; #} displayNames(); if (! $opt_t) { renameFiles(); } ########### renameFiles ########################### # Note: This function uses the global variable %list sub renameFiles { my $use_perl_functions = 0; while (my ($key, $value) = each(%list)) { if ($key ne $value) { if ($use_perl_functions) { rename ($key, $value); } else { my $command = "mv \'$key\' \'$value\'"; system($command); } } } } ########### removeUplinks ########################### # Given a path this will remove all occurances of .. # I: /home/jrsaathoff/docs/../whatever/bill.txt # O: /home/jrsaathoff/whatever/bill.txt sub removeUplinks { my $path = $_[0]; my @dirs = (); for (File::Spec->splitdir($path)) { (($_ eq "..") ? pop @dirs : push @dirs, $_ ); } return File::Spec->catdir(@dirs); } ########### splitPath ########################### # Split the individual file name from the path # it accepts either relative or absolute path names # EG: /home/whatever/file.txt # : /home/whatever/ # : file.txt sub splitPath { (($_[0] =~ m%^(.*/)([^/]*)$%) ? return($1, $2) : return('.', $_[0])); } ########### commonPath ########################### # Return the common path found in all files # EG: /home/jrsaathoff/stuff/whatever.txt and /home/jrsaathoff/shared # R: /home/jrsaathoff # Note: This function uses the global variable %list sub commonPath { my @shareddirs = (); # first find the path with the lowest number of branches foreach my $key (keys %list) { my @dirs = File::Spec->splitdir($key); if ((@shareddirs == 0) || (@shareddirs > @dirs)) { @shareddirs = @dirs; } } my $mismatch = 1; while ($mismatch) { $mismatch = 0; foreach my $key (keys %list) { my @dirs = File::Spec->splitdir($key); if (($dirs[$#shareddirs] ne $shareddirs[$#shareddirs]) && (! $mismatch)) { $mismatch = 1; pop @shareddirs; } } } return File::Spec->catdir(@shareddirs); } ########### displayNames ########################### # Note: This function uses the global variable %list sub displayNames { # All the path names are absolute. We want to find the highest branch # that all paths share. my $sharedpath = commonPath(); print "Common: $sharedpath\n\n"; # The following just determines the number of characters needed to display # each column, and decides the format to display the new file names my ($col1, $col2, $col) = (0, 0, 0); my ($printrow, $i, $color) = (0, 0, 0); while (my ($key, $value) = each(%list)) { if (length($key) > $col1) { $col1 = length($key); } if (length($value) > $col2) { $col2 = length($value); } } $col1 -= length($sharedpath); $col2 -= length($sharedpath); $col = `tput cols`; $printrow = $col < $col1 + $col2 + 2; foreach my $key (sort keys %list) { $i++; # Remove the part of the path that is common in all the files my $pathdiff1 = substr $key, length($sharedpath); my $pathdiff2 = substr $list{$key}, length($sharedpath); # Determine the color to display the file and enable testing mode if errors were found $color = 0; # Red if the new name exists or multiple files are mapped to the same name # This changes color to red if the file name is unchanged, but that will be reverted next if (exists ($list{$list{$key}})) { $color = -1; } foreach my $newkey (keys %list) { if (($key ne $newkey) && ($list{$key} eq $list{$newkey})) { $color = -1; } } # Green if there is no change if ($key eq $list{$key}) { $color = 1; } switch ($color) { case "0" { $color = "\033[0m"; # Black } case "1" { $color = "\033[32m"; # Green } case "-1" { $color = "\033[31m"; # Red $opt_t = 1; } } # Now display the old and new filename on the terminal print $color; if ($printrow) { print $i % 10, ": $pathdiff1\n"; print " : $pathdiff2"; } else { my $format = '%'. ($col1 - length($pathdiff1)) . "s %s"; print $pathdiff1; printf $format, '', $pathdiff2; } print "\033[0m\n"; } print "\n"; } ########### renameFile ########################### # Return the new name of a file sub renameFile { my ($path, $ofile, $nfile) = (0, 0, 0); ($path, $ofile) = splitPath ($_[0]); $nfile = $ofile; if ($opt_n) { # Handle Naming Option $nfile = ''; my @namearray = split(//, $opt_n); # Split the -n paramter into a character array my @pa = (0, 0, 0); # Parameter Array s,p,e. Tells us which occurence of s,p,e we are on for (my $j=0; $j < length($opt_n); $j++) { switch ($namearray[$j]) { case "s" { # Append the $pa[0] (1st, 2nd, etc) -s parameter onto the new name then increment $pa[0] if (exists($opt_s[$pa[0]])) { $nfile .= $opt_s[$pa[0]]; $pa[0]++; } else { die "ERROR: Missing a -s parameter\n"; } } # This selects everything that matches pattern case "p" { if (exists($opt_p[$pa[1]])) { if ($ofile =~ m/($opt_p[$pa[1]])/) { $nfile .= $1; } $pa[1]++; } else { die "ERROR: Missing a -p parameter\n"; } } # This selects everything between two matching patterns case "e" { if (exists($opt_e[2*$pa[2]]) && exists($opt_e[1+2*$pa[2]])) { if ($ofile =~ m/$opt_e[2*$pa[2]](.*)$opt_e[1+2*$pa[2]]/) { $nfile .= $1; } $pa[2]++; } else { die "ERROR: Missing a -p parameter\n"; } } case "i" { my $format = "%0${opt_d}d"; $nfile .= sprintf($format,$opt_i); $opt_i++; } } } } if ($opt_r) { my $tstring = 's/' . $opt_r . '/g'; # Do search/replace with pattern. I could only get eval "\$nfile =~ $tstring"; # it working using this stupid eval command } if ($opt_w) { $nfile =~ s/ /_/g;} # Replace space with underscore if ($opt_l) { $nfile = lc($nfile);} # Change to lowercase if ($opt_u) { $nfile = uc($nfile);} # Change to uppercase if ($opt_padnumbers) { # Change all integers to fixed number of digits EG: test1 to test001 my $format = "%0${opt_d}d"; $nfile =~ s/(\d+)/sprintf($format, $1)/ge; } return $path . $nfile; } ########### USAGE ########################### # Print help and exit program sub usage { print <