Beefy Boxes and Bandwidth Generously Provided by pair Networks chromatic writing perl on a camel
Welcome to the Monastery
 
PerlMonks  

split file on blank lines and match blocks

by raggmopp (Novice)
on Sep 09, 2012 at 02:43 UTC ( #992552=perlquestion: print w/ replies, xml ) Need Help??
raggmopp has asked for the wisdom of the Perl Monks concerning the following question:

Hi all. Working with perl 5.8.8 installed on OEL 5.

I am trying to parse a Nagios formatted hosts.cfg file. Each host is separated from the other with a blank line so there is a block related to each host. Each host is also defined with the hostgroups it is a member of.

I am having a hard time trying to figure out what I am missing and the worse part is I'm sober.

A snippet of the data

define host{ use HALF host_name denlas02 alias denlas02 address 146.xxx.xxx.xxx hostgroups LINUX,DEN,DMZ } define host{ use HALF host_name ppplas11 alias ppplas11 address 10.xxx.xxx.xxx hostgroups LINUX,PPO,ORAPRD } define host{ use ALIVE host_name ppplas12 alias ppplas12 address 10.50.33.26 hostgroups LINUX,GRID,RMAN,CRIT }

I have been trying and trying to identify hosts that are members of the CRIT hostgroup, or of the DMZ hostgroup, or the ... The best I have come up with is to print the line that matches. But I'm looking for the host_name and address in addition to the line.

One of my latest attempts

#!/usr/local/bin/perl use strict; use warnings; my $file = 'hosts.cfg'; open (MYHOSTS, $file); while (<MYHOSTS>) { my @hosts = split(/\n\n/); foreach my $host (@hosts) { print if ('$host' =~ m/(GRID)/); print if grep(/RMAN/, $host); } } close (MYHOSTS);

I've tried with grep and regex, no joy. What is it I'm missing?

Many thanks!

Comment on split file on blank lines and match blocks
Select or Download Code
Re: split file on blank lines and match blocks
by mbethke (Hermit) on Sep 09, 2012 at 03:20 UTC

    Setting the record separator would help: $/ = "\n\n";. After that, a while(<>) { ... } will loop over individual records which you can then dissect using regexps.

    However, that would be a hack as it doesn't follow the actual syntax of the Nagios file but exploits the fact that someone nice has put exactly one blank line between all records. Should you or your admin forget that one day, Nagios will still work but your script won't. I'm not quite sure if there are any subtleties such as escape characters to Nagios' config file syntax but I think it's pretty simple. I'd probably slurp the whole file into a string and then parse blocks with a regexp, using further matching on the whole block to find the relevant pieces of data:

    my $file = do { local $/; <$filehandle> }; while($file =~ / define \s+ host \s* { (.*?) } \s* /gsx) { my ($hostname) = $1 =~ /host_name\s+(\S+)/; ... }
Re: split file on blank lines and match blocks
by BrowserUk (Pope) on Sep 09, 2012 at 03:29 UTC
    trying to identify hosts that are members of the CRIT hostgroup, or of the DMZ hostgroup

    Ignoring that your text say's that, and your code 'GRID|RMAN', this might set you on your road:

    #! perl -slw use strict; $/ = ''; ## para model see perldoc -q paragraphs m[ (?=^.*(DMZ|CRIT)) (?=^.*host_name\s+(\S+)) (?=^.*address\s+(\S+)) ]sx and print "$2 [$3] is a member of $1" while <DATA>; __DATA__ [snip... your sample data here]

    Produces:

    C:\test>junk denlas02 [146.xxx.xxx.xxx] is a member of DMZ ppplas12 [10.50.33.26] is a member of CRIT

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    RIP Neil Armstrong

Re: split file on blank lines and match blocks
by 2teez (Priest) on Sep 09, 2012 at 03:41 UTC
    ...But I'm looking for the host_name and address in addition to the line...
    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; my %host_add; my ( $host, $name ); while (<DATA>) { s/^\s+|\s+$//; if (/^host_name/) { ( $host, $name ) = split /\s+/, $_; if ( !exists $host_add{$name} ) { undef $host_add{$name}; } else { next } } elsif ( /^address/ or /^hostgroups/ ) { my ( $add, $address ) = split /\s+/, $_; push @{ $host_add{$name} }, $address; } } print Dumper \%host_add; __DATA__ define host{ use HALF host_name denlas02 alias denlas02 address 146.xxx.xxx.xxx hostgroups LINUX,DEN,DMZ } define host{ use HALF host_name ppplas11 alias ppplas11 address 10.xxx.xxx.xxx hostgroups LINUX,PPO,ORAPRD } define host{ use ALIVE host_name ppplas12 alias ppplas12 address 10.50.33.26 hostgroups LINUX,GRID,RMAN,CRIT }
    output
    $VAR1 = { 'denlas02' => [ '146.xxx.xxx.xxx', 'LINUX,DEN,DMZ' ], 'ppplas11' => [ '10.xxx.xxx.xxx', 'LINUX,PPO,ORAPRD' ], 'ppplas12' => [ '10.50.33.26', 'LINUX,GRID,RMAN,CRIT' ] };
    With the above as output, you can get the host, hostgroup, and address..
    Add the following to the script like so:
    for my $val ( keys %host_add ) { for ( @{ $host_add{$val} } ) { print $val, q{ }, join " ", @{ $host_add{$val} }, $/ if $_ =~ m{CRIT|DMZ}; } }
    Then, your output will be:
    denlas02 146.xxx.xxx.xxx LINUX,DEN,DMZ ppplas12 10.50.33.26 LINUX,GRID,RMAN,CRIT
    If you tell me, I'll forget.
    If you show me, I'll remember.
    if you involve me, I'll understand.
    --- Author unknown to me
Re: split file on blank lines and match blocks
by kcott (Abbot) on Sep 09, 2012 at 10:26 UTC

    As the data within define host{ ... } are effectively key/value pairs, you can capture that into a Perl hash then directly access host_name, address, etc. as required.

    #!/usr/bin/env perl use strict; use warnings; my $define_re = qr{ \A define \s+ host \{ \s+ ( .*? ) \s+ \} \s* \z }m +sx; my $crit_dmz_re = qr{ (?> CRIT | DMZ ) }x; my $out_format = qq{%s (%s) in groups: %s\n}; local $/ = ''; while (<DATA>) { /$define_re/; my %block = ( split /\s+/ => $1 ); if ($block{hostgroups} =~ /$crit_dmz_re/) { printf $out_format => @block{qw{host_name address hostgroups}} +; } } __DATA__ define host{ use HALF host_name denlas02 alias denlas02 address 146.xxx.xxx.xxx hostgroups LINUX,DEN,DMZ } define host{ use HALF host_name ppplas11 alias ppplas11 address 10.xxx.xxx.xxx hostgroups LINUX,PPO,ORAPRD } define host{ use ALIVE host_name ppplas12 alias ppplas12 address 10.50.33.26 hostgroups LINUX,GRID,RMAN,CRIT }

    Output:

    $ pm_hosts_parse.pl denlas02 (146.xxx.xxx.xxx) in groups: LINUX,DEN,DMZ ppplas12 (10.50.33.26) in groups: LINUX,GRID,RMAN,CRIT

    -- Ken

Re: split file on blank lines and match blocks
by remiah (Hermit) on Sep 09, 2012 at 11:37 UTC

    Hello.

    Another with flip-flop

    #!/usr/bin/perl use strict; use warnings; my ($buff,$hostname,$address); my $criteria="DMZ|CRIT"; while(<DATA>){ #flip-flop. while /begin/ to /end/ condition, you enter the if + block #and line number is set to $num. if(my $num = /define\s+host\{/ .. /\s*\}/ ){ $buff ='' if($num==1); #for regex match keep text to $ +buff $buff .=$_; if( /\s*host_name\s+(.*)/ ){ #get hostname $hostname=$1; } if( /\s*address\s+(.*?)/ ){ #get address $address= $1; } #E0 shows end of Block. if $buff has $criteria, print if ($num =~ /E0/ && $buff =~ /$criteria/s ){ print "$address $hostname is in $criteria \n"; } } } __DATA__ same data with yours...
    For flip-flop, please see this thread, which is very similar case with yours, and Grandfather's meditation for flip-flop and Range Operators at PerlOp.

    One another approach with grep. But this may not work well, I am sure.

    egrep -A 2 -B 5 "hostgroups.*(CRIT|DMZ)" your_config_file

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://992552]
Approved by Athanasius
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others lurking in the Monastery: (14)
As of 2014-04-16 15:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (431 votes), past polls