0

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
GMB
  • 216,147
  • 25
  • 84
  • 135

3 Answers3

1

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
GMB
  • 216,147
  • 25
  • 84
  • 135
  • Thanks GMB. I'm using a script to search and replace the contents descried above. How to use the one-liner in the script. It's a perl script. – Rajesh Murugesan Sep 01 '19 at 20:47
  • @RajeshMurugesan: oh ok, that was not explained in your original question... You would probably need to update your question and show the relevant part of your script. Or you could also have a look at the perlfaq link in my answer, which provides more examples. – GMB Sep 01 '19 at 21:49
1

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
Shawn
  • 47,241
  • 3
  • 26
  • 60
  • This is fine for a one run script. However, if you want to use the flip-flop in something that possibly runs more than once, you want to make sure you avoid this pitfall: https://stackoverflow.com/questions/2143554/is-perls-flip-flop-operator-bugged-it-has-global-state-how-can-i-reset-it (still relevant) – Holli Sep 02 '19 at 16:47
1

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!
Holli
  • 5,072
  • 10
  • 27