#!/usr/bin/perl use Getopt::Std; # TextRep - A find and replace tool for multiple files in one directory $version = 0.3; # CHANGLOG # 0.3 - added recursive find and replace (cool!) # 0.2.1 - added case insensitive switch (-i) # 0.2 - added auto mode and sureness checking for replace mode # 0.1 - basic working script. no apparent bugs. # TO DO # more error checking # exclude files # recursive? # feedback -> ######## [the match string] ######## # prompt on replace - per each replacement $script_name = 'textrep.pl'; #Files to exclude. Files are NOT full path names: @excludefiles = ('.' , '..', $script_name); $tot_match = 0; $tot_files = 0; $files_match = 0; %options; getopt('frdn', \%options); # HELP TEXT =============== if ($options{'h'}) { print <<__END__; USAGE: perl textrep.pl -f [regex string pattern to Find] -r [string to Replace with] -d [Directory to look in. defaults to script directory] -n [only file Names that match regex pattern] -q (quiet mode. suppress text output) -x (locate, but do not replace) -V (version number) -a (automated mode. will not prompt for anything. use to run non-interactively) -i (case insensitive match) -R (recursive search) Option parameters can be 'single quoted' if they contain spaces. If you need to match quotes, be sure to use shell-escapes. Do not use regular expression meta-characters without perl escapes. __END__ exit; } # ========================= # VERSION TEXT =============== if ($options{'V'}) { print "\nTEXTREP VERSION $version \n\nCreated by leiavoia. \nLast updated: Jan 21, 2005\nNot Respnsible for damage, theft, birth defects, or unexplained fires. TextRep is not proven to prevent, cure, or treat any condition or disease. Any positive effects are purely coincidental. TextRep is not intended for internal use. Women who pregnant or are hoping to become pregnant can still use TextRep. Viewer discretion is advised.\n\n"; exit; } # ========================= # REPLACE SURE? =============== if (!$options{'x'} and !$options{'a'} ) { print "\nYou have not specified a do-not-replace flag (-x).\nAre you sure you want to replace all matches? [y/n] "; while(<>) { chomp(); if ( lc($_) eq 'y' ) { last; } elsif ( lc($_) eq 'n' ) { print "Exiting...\n\n"; exit; } else { print 'Yes or No? [y/n]: '; next;} } } # ========================= # set defaults if (exists $options{'d'}) {$options{'d'} =~ s/\/$//;} else {$options{'d'} = '.';} #take off trailing slash unless (exists $options{'n'}) {$options{'n'} = '.*';} #Start the Directory Tracker. #Push and pop to modify the current branch of scanning. @DirTracker = $options{'d'} || "."; sub ReadDir { # Recursive engine for scanning dirs. Spits out input to shell #mush DirTracker together for a good directory name: my $thisdir = join ('/', @DirTracker); print "[ Now scanning $thisdir . . . ] \n"; #get the current dir's contents: local(*CURRENT); opendir(CURRENT, "$thisdir"); my @thisdirinfo = readdir CURRENT; closedir CURRENT; #decide what to do with what we find foreach $i (@thisdirinfo) { if( Exclude($i) ) {next} # skip if it matches an exclude file my $fullpath = "$thisdir/$i"; if ( -d $fullpath ) { # if directory if ( $options{'R'} ) { # if recursive push (@DirTracker, $i); #put the dir to search next in the tracker ReadDir(); } else { next } } else { # if it's a text file and the file matches the file-naming pattern my $pattern = $options{'n'}; if( (-T $fullpath) and ($i =~ /$pattern/) ) { Replace( $fullpath ); } } } #Remove this directory from the tracker to go back up a level: pop @DirTracker; } ######### START ############## ReadDir(); # #get the current dir's contents: # local(*CURRENT); # opendir(CURRENT, $options{'d'}); # my @thisdirinfo = readdir CURRENT; # closedir CURRENT; # # print "\n"; # # foreach $i (@thisdirinfo) { # if ( ($i eq '.') || ($i eq '..') || ($i eq $script_name) || (-d ($options{'d'} . '/' . $i)) ) {next;} # $pattern = $options{'n'}; # if( (-T ($options{'d'} . '/' . $i)) and ($i =~ /$pattern/) ) {Replace($i);} # } # print report and quit print "\n"; print "FILES SCANNED: $tot_files \n"; print "FILES W/ MATCHES: $files_match \n"; print "TOTAL MATCHES FOUND: $tot_match \n"; if ($options{'x'}) { print "*** no matches were replaced *** \n"; } print "\n"; exit; #//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// sub Replace { $f = $_[0]; $tot_files++; print "\tScanning $f \n" unless $options{'q'}; local $/ = undef; # local slurp mode my $incoming; local(*FILE); open(FILE, '<' . $f) || die("Could not open " . $f); while() { $incoming = $_;} close FILE; #do substitutions - but check for case insensitive $F = $options{'f'}; $R = $options{'r'}; if ($options{'i'}) { $changed = ($incoming =~ s/$F/$R/gi); } else { $changed = ($incoming =~ s/$F/$R/g); } # bail out if no matches found if ($changed == 0) { return 0; } # re-write the file if needed if (! $options{'x'}) { open(FILE, '>' . $f) || die("Could not open " . $f); print FILE $incoming; close FILE; } print "\t\t$changed matches \n" unless $options{'q'}; $tot_match += $changed; $files_match++; return 1; } sub Exclude { #Determines if files to exclude are hit #Takes: -- #Returns: 1 if exclude file was matched, 0 if not. foreach $i (@excludefiles) { if ($i eq $_[0]) {return 1} } return 0; } #////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////