cxfcxf has asked for the wisdom of the Perl Monks concerning the following question:

hello,monks i am trying to remove matched and ranged lines from a file what first come to my mind is range operator like .. ... the file is like
define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.1 } define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.11 } define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.21 } define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.1 }
this is the file. i want to remove the whole block like
define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.1 }
so i wrote a scripts like
open(NAG,"nagios") or dir $!; my $start = qr/^define/; my $end = qr/^\}$/; my @array; while (<NAG>) { if (/$start/ .. /$end/) { push @array, $_; } } foreach (@array) { if (/192.168.1.1/) { print $_; } }
it just print one line which matchs 192.168.1.1.... so i recognized $_ in while (<NAG>) is just a line... i cant use range operator here... are there any way to reach the goal?

Replies are listed 'Best First'.
Re: how to remove matched multi lines
by jwkrahn (Abbot) on Jul 25, 2009 at 02:30 UTC
    open NAG, '<', 'nagios' or die "Cannot open 'nagios' $!"; $/ = "}\n"; while ( <NAG> ) { print if /\b192\.168\.1\.1\b/; }
Re: how to remove matched multi lines
by bichonfrise74 (Vicar) on Jul 25, 2009 at 04:46 UTC
    Something like this?
    #!/usr/bin/perl use strict; $/ = "}"; while ( my $block = <DATA>) { print $block if ( $block !~ /\b\Q192.168.1.1\E\b/ ); } __DATA__ define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.1 } define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.11 }

    Update: Oops, I didn't see the answer of jwkrahn, so ignore this.
Re: how to remove matched multi lines
by Marshall (Canon) on Jul 27, 2009 at 23:34 UTC
    Other specific solutions are great. But I got onto a different track as I think the idea of the range ... operator is a good one as this leads to a general solution that can be used to sort the input records by some arbitrary input criteria, etc. The idea being to make an Array of Hash and then process that...

    I got this far with some code. It works fine except for some reason, this code requires a "blank line" between the define {....} records otherwise a record gets missed in the final array. I appear to be in some sort of "brain lock" right now! The solution has to be simple, and I've gotten some hints, but I'm not able to see it! Oh, yes I do know that my deleteDB() sub isn't the fastest critter on the block.

    define host{ use windows-server .... } define host{ use windows-server } is ok, BUT define host{ use windows-server .... } define host{ use windows-server } is not ok!
    #!usr/bin/perl -w use strict; use Data::Dumper; my @DB; my %hash = (); while (<DATA>) { if ( m/^define/.../}\s*$/) { next if /{\s*$/; next if /\s*\}\s*/; my ($hkey,$hval) = split; $hash{$hkey}=$hval; } else { push @DB, {%hash}; } } push @DB,{%hash}; print Dumper \@DB; #Array of Hash has been created deleteDB ('address' => '192.168.1.1'); #delete one record sub deleteDB { my ($key,$value) = @_; @DB = grep {$_->{"$key"} ne "$value"} @DB; } print "=============================\n"; print Dumper \@DB; #Prints: #$VAR1 = [ # { # 'use' => 'windows-server', # 'hostgroups' => '060202', # 'host_name' => 'serverA', # 'address' => '192.168.1.1', # 'alias' => '060202', # 'contact_groups' => 'yu' # }, # { # 'use' => 'windows-server', # 'hostgroups' => '060202', # 'host_name' => 'serverA', # 'address' => '192.168.1.11', # 'alias' => '060202', # 'contact_groups' => 'yu' # }, # { # 'use' => 'windows-server', # 'hostgroups' => '060202', # 'host_name' => 'serverA', # 'address' => '192.168.1.21', # 'alias' => '060202', # 'contact_groups' => 'yu' # } # ]; #============================= #$VAR1 = [ # { # 'use' => 'windows-server', # 'hostgroups' => '060202', # 'host_name' => 'serverA', # 'address' => '192.168.1.11', # 'alias' => '060202', # 'contact_groups' => 'yu' # }, # { # 'use' => 'windows-server', # 'hostgroups' => '060202', # 'host_name' => 'serverA', # 'address' => '192.168.1.21', # 'alias' => '060202', # 'contact_groups' => 'yu' # } # ]; __DATA__ define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.1 } define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.11 } define host{ use windows-server host_name serverA hostgroups 060202 alias 060202 contact_groups yu address 192.168.1.21 }

      It is instructive to look at what the flip-flop operator actually generates. Consider:

      use strict; use warnings; my $str = '[][ab]c[]d'; for my $chr (split '', $str) { my $result = $chr eq '[' .. $chr eq ']'; printf "%-2s %s\n", $chr, $result; }

      Prints:

      [ 1 ] 2E0 [ 1 a 2 b 3 ] 4E0 c [ 1 ] 2E0 d

      Note the iteration count for 'true' and that the last iteration has a trailing 'E0'. Your 'missing blank line' problem is just like the '][' case above - you need to either retest the ending condition and exclude it, or you can save the flip-flop result and test that.

      If you haven't seen it already, you may find Flipin good, or a total flop? of interest too.


      True laziness is hard work