Reputation: 179
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
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
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;
}
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
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