Rajesh Murugesan
Rajesh Murugesan

Reputation: 1

Search and replace a string in a file

I'm trying to read contents from an input file, copy only certain lines of code from the file and print in an output file.

Certain lines of code is determined by:

  1. Code name to determine the first line (IP1_NAME or IP2_NAME)
  2. Pattern to determine the last line (END_OF_LIST)

Input file:

IP1_NAME    
   /ip1name/ip1dir/ //CLIENT_NAME/ip1name/ip1dir
   /ip1testname/ip1testdir/ //CLIENT_NAME/ip1testname/ip1testdir
END_OF_LIST
IP2_NAME
   /ip2name/ip2dir/ //CLIENT_NAME/ip2name/ip2dir
   /ip2testname/ip2testdir/ //CLIENT_NAME/ip2testname/ip2testdir
END_OF_LIST

Output file:

(If IP1_NAME is chosen and the CLIENT_NAME should be replaced by tester_ip)

/ip1name/ip1dir/ //tester_ip/ip1name/ip1dir
/ip1testname/ip1testdir/ //tester_ip/ip1testname/ip1testdir

Upvotes: 0

Views: 68

Answers (3)

Holli
Holli

Reputation: 5082

I am assuming there is additional stuff in your input file, otherwise we would not have to jump through the hoops with these start and end markers as and we could just say

perl -ne "print if /^ /"

and that would be silly, right ;-)

So, the flipflop has potential problems as I stated in my comment. And while clever, it does not buy you that much in terms of readability or verbosement (verbocity?), since you have to test again anyway in order to not process the marker lines.

As long as there is no exclusive flip flop operator, I would go for a more robust solution.

my $in;

while (<DATA>) {
    $in = 1, next if /^IP\d_NAME/;
    $in = 0       if /^END_OF_LIST/;

    if ( $in )
    {
        s/CLIENT_NAME/tester_ip/;
        print;
    }
}
__DATA__
cruft
IP1_NAME    
   /ip1name/ip1dir/ //CLIENT_NAME/ip1name/ip1dir
   /ip1testname/ip1testdir/ //CLIENT_NAME/ip1testname/ip1testdir
END_OF_LIST
more
cruft
IP2_NAME
   /ip2name/ip2dir/ //CLIENT_NAME/ip2name/ip2dir
   /ip2testname/ip2testdir/ //CLIENT_NAME/ip2testname/ip2testdir
END_OF_LIST
Lore Ipsargh!

Upvotes: 1

Shawn
Shawn

Reputation: 52579

As a script instead of a one-liner, using the scalar range operator:

#/usr/bin/env perl
use warnings;
use strict;
use autodie;
use feature qw/say/;

process('input.txt', qr/^IP1_NAME$/, qr/^END_OF_LIST$/, 'tester_ip');

sub process {
  my ($filename, $startpat, $endpat, $newip) = @_;
  open my $file, '<', $filename;
  while (my $line = <$file>) {
    chomp $line;
    if ($line =~ /$startpat/ .. $line =~ /$endpat/) {
      next unless $line =~ /^\s/; # Skip the start and lines.
      $line =~ s/^\s+//; # Remove indentation
      $line =~ s/CLIENT_NAME/$newip/g; # Replace with desired value
      say $line;
    }
  }
}

Running this on your sample input file produces:

/ip1name/ip1dir/ //tester_ip/ip1name/ip1dir
/ip1testname/ip1testdir/ //tester_ip/ip1testname/ip1testdir

Upvotes: 1

GMB
GMB

Reputation: 222652

You could use the following one-liner to pull out the lines between the two patterns:

perl -0777 -ne 'print "$1\n" while /IP1_NAME(.*?)END_OF_LIST/gs' in.txt > out.txt

Where in.txt is your input file and out.txt is the output file.

This use case is actually described in perlfaq6: Regular Expressions.

You can then modify the output file to replace CLIENT_NAME with tester_ip:

perl -pi -e 's/CLIENT_NAME/tester_ip/' y.txt

Upvotes: 1

Related Questions