#!/usr/bin/perl -w ################################################# # sarep 1.1 (991119) - Search and Replace tool # # (C)1999 Matthew Borowski # # http://tarp.worldserve.net/software/ # # ftp://ftp.worldserve.net/pub/sarep/ # ################################################# # Package dependencies: # Text::CSV_XS used with the option -f to parse CSV files # ftp://ftp.cpan.org/pub/CPAN/modules/by-category/11_String_Lang_Text_Proc/Text/Text-CSV_XS-0.20.tar.gz # # Reads files one line at a time and modifies the text, supports regular # expressions. Works with wildcards, supports multiple files on command # line. Run sarep with no arguments for help. # # Changes from 1.0 to 1.1: # Big thanks go once again to Andrey A. Chernov. Two patches by him: # * sarep will now maintain file creation date on files it does not # modify. The previous code actually touched each file, thus resetting # the file date. # * sarep now uses the perl locale support, so case insensitivity now # works with non-english languages as well # # Changelog from 0.5 to 1.0: # # * Nice new version number! # * New seperate -v (Verbose) and -vv (VeryVerbose) modes # * SAREP now preserves file permissions when modifying files # * TODO List: report number of changes made instead of number of # lines changed. # # Many Thanks to the Contributors: # Raphaël Rousseau # Andrey A. Chernov # Doughnut # Thanks to Bert Vermeulen and the #LinuxOS guys # # Comments/questions appreciated. No flames, I'm a newbie programmer # License: BSD License use strict; use locale; my ($renametonormal, $option, $searchfor, $replacewith, $fromfile, $patterns, $verbose, $veryverbose, $patternfile, $currentnum, $file, $occurrences, $line, $caseinsensitive, $escapemeta); #START #if no command-line argument, print usage info &helpinfo if ($#ARGV eq -1); # set defaults. If this gets any more complicated # consider switching to Getopt::Std or Getopt::Long. $renametonormal=1; # Do we rename file to their initial name ? $caseinsensitive = 0; # Is the search case sensitive ? $escapemeta = 0; # Do we escape metacharacters ? $fromfile = 0; # Do we take search and replace strings from a CSV file ? $verbose = 0; # Do we need verbosity ? $veryverbose = 0; # Do we want to be very verbose? while (defined($ARGV[0]) && $ARGV[0] =~ /^-/) { $option = shift @ARGV; $renametonormal=0 if $option =~ /n/; $caseinsensitive = 1 if $option =~ /i/; $verbose = 1 if $option =~ /v/; #Very verbose mode implies standard verbose mode as well $verbose = 1 if $option =~ /vv/; $veryverbose = 1 if $option =~ /vv/; $escapemeta = 1 if $option =~ m#m#; if ($option =~ /f/) { $fromfile = 1; $patternfile = shift @ARGV; &helpinfo if (! -f $patternfile); } &helpinfo if ($option =~ /-[^vfinm]/) ; } if (!$fromfile) { #check if they included a search-string, if not exit &helpinfo if (!defined($searchfor = shift @ARGV)); #check if they included a replace-string, if not exit &helpinfo if (!defined($replacewith = shift @ARGV)); $patterns->{$searchfor} = $replacewith; } else { require Text::CSV_XS; my $csv = Text::CSV_XS->new({'binary' => 1}); my ($line); open (PATTERN, $patternfile) || die "Cannot open pattern file $patternfile : $!"; while (defined ($line = )) { if ($csv->parse($line)) { my @fields = $csv->fields; $patterns->{$fields[0]} = $fields[1]; } else { my $err = $csv->error_input; die 'parse() failed in file '.$patternfile.' on argument: '.$err.'\n'; } } } #check if they included at least ONE filename. &helpinfo if !defined($ARGV[0]); # Apply changes in each file while (defined($file = shift @ARGV)) { &replace ($patterns, $file); } sub replace { my ($patterns, $file) = @_; my $fmode; my $ftime; my @st; # prepare to count number of changes made. $occurrences = 0; #print current file being sarep'ed. print "Processing file $file:" if $verbose; #open up the file, if it doesnt exist, print an error open(INFILE, "<$file") || die("Error: $file $!"); if ($renametonormal) { if (@st = (stat(*INFILE))) { $fmode = $st[2]; $ftime = $st[9]; } else { die("Error: can't stat $file: $!"); } } #now open up the filename with a .sarep extension. open(OUTFILE,">$file\.sarep") || die("Error: unable to create temporary .sarep file"); #while INFILE still has lines, make $_ equal to the line, then #search/replace it and write it to the OUTFILE. The $_ is removed from #INFILE, so when you go through the loop again, the next line is done. while() { $line = $_; my ($searchfor, $replacewith); foreach $searchfor (keys %$patterns) { $replacewith = $patterns->{$searchfor}; $searchfor = quotemeta($searchfor) if $escapemeta; $searchfor = '(?i)'.$searchfor if $caseinsensitive; #print current line being sarep'ed. print "\n \ts/$searchfor/$replacewith/g" if $veryverbose; #print out what string is being searched for and replaced with, and if #the results will be printed out to the filename, or to filename.sarep. if($renametonormal) { print ", output directly to file(s)." if $veryverbose; } else { print ", output to .sarep file(s)." if $veryverbose; } $occurrences++ if $line =~ s/$searchfor/$replacewith/g; } print OUTFILE $line; } #close up the files... close OUTFILE; close INFILE; #if they didnt specify -n, then $renametonormal=1 (due to the if at the #top_. so go ahead and rename the .sarep file to the normal filename. if($renametonormal){ rename "$file\.sarep",$file; # chmod _after_ rename since rename clears s-bit on some system$ if (chmod ($fmode, "$file") != 1) { $fmode = sprintf "0%o", $fmode; die("Error: can't chmod $fmode $file: $!"); } if ($occurrences == 0) { if (utime (time, $ftime, "$file") != 1) { die("Error: can't set times for $file: $!"); } } } print "\n" if $veryverbose; # I'd rather this said how many changes were made, but not tonight. print " $occurrences lines altered.\n" if $verbose; } #subroutine to print help sub helpinfo { print STDERR "usage: $0 search-string replace-string filename(s)\n"; print STDERR "options:\n\t-n (create new files with .sarep extension)\n"; print STDERR "\t-i (performs the search as case-insensitive)\n"; print STDERR "\t-m (performs the search with metacharacters interpreted as normal ones)\n"; print STDERR "\t-f (takes search and replace strings from the file)\n"; print STDERR "\t-v (verbose mode)\n"; print STDERR "\t-vv (very verbose mode)\n"; exit 1; # Exit with value different from 0 which means ERROR } #END