#!/usr/bin/perl -w use strict; unless( $#ARGV >= 1 ) { print < ... Renames a series of files to names of the form: /_. The number has as many digits as needed, the name is determined by the names given in the first argument and the extension is the same as the original file names. The files' paths are also retained, so you can use this to rename files across several directories. The given new names are treated as follows: All words except "in", "the" and similar are capitalised, and sequences of characters which are neither letters nor digits are replaced by a single underscore. The names are given in a single command-line argument separated by commas and may not themselves contain commas. The option -n merely prints what would have been done rather than renaming anything. -r reverses the sense of the operation by renaming the newly constructed names to the original files. It can be used to undo a previous run, but be aware that shell wildcards do not work for files which no longer exist. If you think you may want to reverse the renaming later, and if you are using bash, you can use Ctrl-x * or Alt-{ to perform the wildcard expansion on the command line so the expanded list can be retrieved from the command history. EOF exit (!$ARGV[0] || ($ARGV[0] ne "--help" && $ARGV[0] ne "-h")); } my @nocaps= qw(the a an in of at for not); my $dummyrun= 0; my $reverserun= 0; while( $ARGV[0] eq "-n" or $ARGV[0] eq "-r" ) { $dummyrun= 1 if $ARGV[0] eq "-n"; $reverserun= 1 if $ARGV[0] eq "-r"; shift @ARGV; } my @names= split /\s*,\s*/, shift(@ARGV); if( $#names != $#ARGV ) { print STDERR "You gave ", $#names+1, " names but ", $#ARGV+1, " files! Aborting\n"; exit 1; } my $havedirs= 0; my $havelinks= 0; for my $file (@ARGV) { if( !$reverserun && !-e $file ) { print STDERR "There is no file named $file. Aborting.\n"; exit 1; } if( !-f $file ) { $havedirs= 1 if -d $file; $havelinks= 1 if -l $file; } my @same= grep($_ eq $file, @ARGV); if( $#same ) { print STDERR "File $file given more than once! Aborting.\n"; exit 1; } } if( $havedirs || $havelinks ) { print "Warning: The file list given contains symbolic links and/or directories.\nRerun with -r if this was unintentional.\n"; } my $log10e= 0.43429448190325182765; my $num= "0" x (int($log10e*log(@ARGV))+1); ++$num; my %map; my @news; for my $orig (@ARGV) { my $path= ""; $orig =~ /^(.*\/)[^\/]*$/ and $path= $1; my $ext= ""; $orig =~ /^.*(\.[^.]*)$/ and $ext= $1; my $name= shift @names; my @words= split /[^a-zA-Z0-9]+/, $name; shift @words unless $words[0]; pop @words unless $words[$#words]; $words[0]= ucfirst($words[0]); for my $word (@words[1..$#words]) { $word= ucfirst($word) unless grep($_ eq $word, @nocaps); } $name= $path . $num . "_" . join("_", @words) . $ext; if( !$reverserun && -e $name ) { print STDERR "Target file name $name already exists. Aborting.\n"; exit 1; } elsif( $reverserun && !-e $name ) { print STDERR "File name $name does not exist. Cannot reverse renaming.\n"; exit 1; } $map{$orig}= $name; push @news, $name; ++$num; } %map= reverse %map if $reverserun; my @origs= $reverserun? @news: @ARGV; if( $dummyrun ) { map { print "Not renaming $_ to ", $map{$_}, "\n"; } @origs; } else { for my $orig (@origs) { if( !rename($orig, $map{$orig}) ) { print STDERR "Could not rename $orig to ", $map{$orig}, "\n"; print STDERR "Undoing completed renamings.\n"; my $err= 0; for my $done (@origs) { last if $done eq $orig; $err ||= !rename $map{$done}, $done; } if( $err ) { print STDERR "There were errors restoring the prior names. Sort the mess out yourself.\n"; } exit 1; } } }