#! \usr\bin\perl ############################################################################### ############################################################################### # # To rename files or folders recursively using regular expressions # by Andrew Hardwick # ############################################################################### ############################################################################### # Version 1, 2002/3/24 # Version 2, 2005/8/31 # Total rewrite. # Takes expressions from the command line instead of needing code altering. # Takes command line options. # Defaults to CWD instead of Temp directory. # Renamed from 'TemplateForFileRenaming.pl' to 'Rename.pl'. # Version 3, 2005/12/20 # Debugged changing names in subdirectories # Version 4, 2006/9/8 # Allowed empty strings as search & replace parameters. # Version 5, 2007/4/8 # Added option to turn off recursion. # Tidied source code & added check for overwriting when renaming. # Version 6, 2007/4/9 # Improved to allow renaming of directories & their contents in the same run. # Version 7, 2007/4/10 # Minor help message typo corrections. ############################################################################### ############################################################################### # How To Use # Run with no arguments or '-h' option to get instructions. # Warning: As it 'eval's the regular expressions to allow full functionality # it can be use to run other programs from within a supplied regular # expression. In its intended use (run from command line by user) # that is not an additional risk but it is not safe to run with parameters # from unsafe sources such as CGI parameters on a public webserver. ############################################################################### ##### This file is formatted for 80 character lines and 4 character tabs. ##### ############################################################################### # Include libraries use File::Find; # For file finding command use Getopt::Std; # For extracting command line options # Disenable automatic global variables use strict; ############################################################################### # Main routine ############################################################################### { # Get command line options my %Options; getopts('hfdn:m:b:et',\%Options); my $HelpFlag=exists($Options{'h'}); my $FilesFlag=exists($Options{'f'}); my $DirectoriesFlag=exists($Options{'d'}); unless($FilesFlag||$DirectoriesFlag) { $FilesFlag=$DirectoriesFlag=1;} my $NameExpression=exists($Options{'n'})?$Options{'n'}:''; my $Modifiers=exists($Options{'m'})?$Options{'m'}:''; my $BaseDirectory=exists($Options{'b'})?$Options{'b'}:'.'; my $NoRecursionFlag=exists($Options{'e'}); my $TestFlag=exists($Options{'t'}); # Get from & to expressions my $FromExpression=shift @ARGV; my $ToExpression=shift @ARGV; # Display help if($HelpFlag||!defined($FromExpression)|| !defined($ToExpression)||shift@ARGV) { print "Rename, Version 7, by Andrew Hardwick.\n"; print "Finds (recursively) & renames files & directories.\n"; print "Usage: rename \n"; print " -h = Print these instructions.\n"; print " -f = Limit to files only (default = -fd).\n"; print " -d = Limit to directories only (default = -fd).\n"; print " -n = Limit to matching filenames.\n"; print " -m [egimosx] = Perl s&r modifiers (default = none).\n"; print " -b = Search within directory (default = ". "'.').\n"; print " -e = Do not recurse subdirectories.\n"; print " -t = Test mode (does not make the changes).\n"; print ", & are Perl regular expressions.\n"; exit;} # Find all files (store names indexed by path) my %FileNamesByPath; find( sub { # Skip the base directory if($_ eq '.') { next;} # Prevent decending into subdirectories if recursion is off if($NoRecursionFlag&&-d) { $File::Find::prune=1;} # Add matching files & directories to the list to rename if(($FilesFlag&&-f||$DirectoriesFlag&&-d)&&/$NameExpression/) { $FileNamesByPath{$File::Find::name}=$_;}} ,$BaseDirectory); # Arrange file paths so deepest level done first # (Since paths to deeper levels may change with higher level directories # being renamed. Doing it this way not simply by 'File::finddepth' # because that not allow pruning unlike 'File::find'.) my @FilePaths=reverse(sort(keys %FileNamesByPath)); # Rename the files my($NameOld,$NameNew); my $Do="/$FromExpression/$ToExpression/$Modifiers"; foreach my $FilePath (@FilePaths) { $NameNew=$NameOld=$FileNamesByPath{$FilePath}; eval("\$NameNew=~s$Do;return 1;") or die "Regexp Error:\n $Do\n$@\n"; { # Check for name changes unless($NameNew eq $ NameOld) { # Report it print "$FileNamesByPath{$FilePath}\n"; print " From: $NameOld\n"; print " To: $NameNew\n"; # Do the renaming unless($TestFlag) { $FilePath=~/^(.*\/)/; rename($FilePath,$1.$NameNew) or print " Error: could not rename '$NameOld'". " to '$NameNew'\n";}}}}} ###############################################################################