简体   繁体   中英

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

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

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

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM