user1060641
user1060641

Reputation: 179

Perl - how to extract multiple lines

I have a text file formatted like this (it's a Cisco firewall config file):

object-group network group21
 network-object host 10.254.3.120
 network-object host 10.254.3.121
 network-object host 10.254.3.122
object-group network group456_local
 network-object host 192.168.150.63
 network-object host 192.168.150.64
 network-object host 192.168.150.76
object-group network group456_proxy
 network-object host 10.241.150.63
 network-object host 10.241.150.64
 network-object host 10.241.150.65
object-group network group154
 network-object host 10.9.11.45
 network-object host 10.9.11.10

Note: Yes, there are spaces in the file in front of network-object group.

Using Perl, how can I extract all lines from the object group if it contains word group456? What I'm trying to do is search the whole config file and if there's a word called group456, the output print should be:

object-group network group456_local
 network-object host 192.168.150.63
 network-object host 192.168.150.64
 network-object host 192.168.150.76
object-group network group456_proxy
 network-object host 10.241.150.63
 network-object host 10.241.150.64
 network-object host 10.241.150.65

Thank you in advance for your help.

Upvotes: 0

Views: 92

Answers (3)

ikegami
ikegami

Reputation: 386676

Generic approach for grouping lines without reading the entire file into memory:

my @buf;
while (1) {
   my $line = <>;
   if (!defined($line) || $line !~ /^ /) {
      if (@buf) {
         ...
      }

      last if !defined($line);

      @buf = ();
   }

   push @buf, $line;
}

In this case, replace the missing part with

print @buf
   if $buf[0] =~ /^object-group\s+network\s+\w*group456/;

It's simpler if you read the entire file into memory.

my $file = do { local $/; <> };

print
   for
      grep { /^object-group\s+network\s+\w*group456/ }
         split(/^(?! )/m, $file);

Unlike the earlier answer, these approaches don't require the blocks to be one after the other, and it doesn't require knowledge of what the next block will be.

Upvotes: 2

Borodin
Borodin

Reputation: 126762

This solution uses the functional but ugly state flag technique

The input file is read a line at a time, and the variable $print (initially false) is set to true if a line is encountered that begins with a non-space character and contains group456

Every line is printed as long as $print is true

Note that use autodie obviates the need for checking whether the open was successful

use strict;
use warnings 'all';
use autodie;

use constant FILE => 'firewall.cfg';

open my $fh, '<', FILE;

my $print = 0;

while ( <$fh> ) {

    $print = /group456/ if /^\S/;

    print if $print;
}

output

object-group network group456_local
 network-object host 192.168.150.63
 network-object host 192.168.150.64
 network-object host 192.168.150.76
object-group network group456_proxy
 network-object host 10.241.150.63
 network-object host 10.241.150.64
 network-object host 10.241.150.65

Upvotes: 1

Hunter McMillen
Hunter McMillen

Reputation: 61540

A simple solution would be to use the .. operator to gather lines between a set start and end point (e.g group2 .. group3+1). Have to go one group further to ensure that all lines are grabbed then remove that last line. This solution assumes that the groups you want to capture will always be in order.

#!perl

use strict;
use warnings;

while (readline(*DATA)) {
  next unless /group2\b/ .. /group4\b/;
  last if /group4/;
  print;
}

__DATA__
object-group network group1
 network-object host 10.254.3.120
 network-object host 10.254.3.121
 network-object host 10.254.3.122
object-group network group2
 network-object host 192.168.150.63
 network-object host 192.168.150.64
 network-object host 192.168.150.76
object-group network group3
 network-object host 10.241.150.63
 network-object host 10.241.150.64
object-group network group4
 network-object host 10.9.11.45
 network-object host 10.9.11.10

You could generalize this in a subroutine that accepted the text, starting group, and ending group.

Upvotes: 1

Related Questions