Phil R Lawrence on Wed, 20 Aug 2003 11:42:52 -0400


[Date Prev] [Date Next] [Thread Prev] [Thread Next] [Date Index] [Thread Index]

ok, I got one


OK, 

like it hasn't been done before... :-)  But I was just dying for a useful global search and replace script while working on something here at work.

Goals:
  - everyone at my shop can use it easily
      e.g. interactive approval of resulting diff
  - I can use it in advanced ways
      e.g. piplining, s///e

I did have trouble parameterizing the s/// modifiers.  Lookaheads seem only viable for the match expression, not the whole s/// operator.  Any clues?  This now works well enough for me to continue on with my real project, so I thought I'd throw it out to you guys for some "extreme programming".  (i.e. please cloo me in as needed, and share your undoubtedly niftier implementations :-)

prl


#!/usr/bin/perl
use warnings;
use diagnostics;
use strict;

use Getopt::Long;
use File::Temp qw/ tempfile /;

my $i;       # analagous to Perl interpreter -i option
my $e;       # this number of e's will be tacked onto end of s///
my $ss;      # search string
my $rs;      # replace string
my $rc;      # replace code, used with the -e flag
my $sp;      # search pattern
my $v;       # verbose
GetOptions (
    'i:s' => \$i,
    'e:n' => \$e,
    'search_string|ss|s=s' => \$ss,
    'replace_string|rs|r=s' => \$rs,
    'replace_code|rc=s' => \$rc,
    'search_pattern|sp=s' => \$sp,
    'pager=s' => \$ENV{PAGER},
    'verbose!' => \$v,
    );

### set variables appropriately ###
# temporary, until I get Term::Interact upgraded to allow
# console interaction without interrupting STDIN/OUT...
my $console = *STDOUT;

$sp = qr/$sp/ if $sp;  # does this really save me anything?
$ss = quotemeta $ss if $ss;
die "No replacement string or code specified\n" unless $rs or $rc;

if ($rc) {
    $e = 1 unless $e;
}

### main logic ###
my ($orig, $new, $bkp);
my $fh_new;
if (defined $i) {
    die "Only one file can be specified if using the -i flag.\n" if $#ARGV;
    die "$ARGV[0] is not a valid file\n" unless -s $ARGV[0];
    $orig = $ARGV[0];
    unless ($i eq '') {
        ($bkp = $i) =~ s/\*/$orig/g; #just like -i with Perl interpreter
    }
    ($fh_new, $new) = tempfile;
} else {
    $fh_new = *STDOUT;
}

# set up regex.  can't figure out how to use a variable for substitution
# modifiers in a regular s///, so instead I have to eval this stringy s///.
my $s = defined $sp ? $sp : $ss;
my $r = defined $rc ? $rc : $rs;
my $m = 'og';
$m .= 'e' x $e if defined $e;
print $console "Substitution will be: s/$s/$r/$m\n" if $v;

while (<>) {
    eval "s/$s/$r/$m";
    print $fh_new $_;
}
close $fh_new;

exit unless defined $i;

print "Press ENTER to view diff of old vs. new ... ";
my $stdin = <STDIN>;
# TODO: use Algo::Diff or something to help poor Win32 folk
my $args = "diff $orig $new"
         . (exists $ENV{PAGER} ? (' | ' . $ENV{PAGER}) : ());
system $args and die "system $args failed: $?";

print "Discard or keep changes? [D|k]: ";
chomp ($stdin = <STDIN>);
if ($stdin eq 'k') {
    if (defined $bkp) {
        rename $orig, $bkp;
        print "Backed up original to $bkp\n";
    }
    rename $new, $orig;
    print "Saved new to $orig\n";
} else {
    unlink $new;
}
-
**Majordomo list services provided by PANIX <URL:http://www.panix.com>**
**To Unsubscribe, send "unsubscribe phl" to majordomo@lists.pm.org**