Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

Need advice on how to break foreach parsing loop into functions

by ewhitt (Scribe)
on Sep 15, 2007 at 14:35 UTC ( [id://639173]=perlquestion: print w/replies, xml ) Need Help??

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

Monks,

I have a script to parse sections of a configuration file using a single foreach loop. This loop performs many matches in two 2 tiers. I want parent tier matches to send the following lines to functions until "!" is matched.
------- Sample Text ------- ! interface Ethernet blah... blah... ! interface Gigabit blah... blah... ! interface Ethernet blah... ! ------- Sample Text -------

I envisioned this to work something like this, but feel this is not the best way to achieve this. For one, in my match statements for "Ethernet" and "Gigabit", I don't know how to advance $line forward to see if we have reached a "!". Could anyone please recommend an approach for something like this. Thanks in advance!
foreach $line (@cfg) { if ($line =~ m/^interface (\.*)/) { if ($1 eq "Ethernet") { &parse_ethernet($1); Keep parsing until $line matches "!" } if ($1 eq "Gigabit") { &parse_gigabit($1); Keep parsing until $line matches "!" } } if ($line =~ m/^system (\.*)/) { blah... } }

Replies are listed 'Best First'.
Re: Need advice on how to break foreach parsing loop into functions
by Anno (Deacon) on Sep 15, 2007 at 15:21 UTC
    Your code has a bug. In the regex in "if ($line =~ m/^interface (\.*)/) {" you have escaped the dot, which isn't what you want. The regex should be "/^interface (.*)/)".

    Less importantly, you are using a leading ampersand in the sub calls "&parse_ethernet($1);" and "&parse_gigabit($1);". They are unnecessary and you shouldn't use them unless you know what they do and want tthe effect. Here, you don't.

    As for your parsing problem, you can set the input separator to "!\n" when you read the file. Then you will have one complete chunk of input to give to any of the subs parse_* or whatever. Note that these must now expect a multiline string containing all the information in the current chunk.

    Here is a sketch:

    $/ = "!\n"; while ( <DATA> ) { chomp; if ( /^interface (.*)/ ) { if ( $1 eq 'Ethernet' ) { parse_ethernet( $_); } elsif ( $1 eq 'Gigabit' ) { parse_gigabit( $_); } } elsif ( /^system (.*)/ ) { # blah... } } sub parse_gigabit { my $chunk = shift; $chunk =~ tr/\n/ /; print "GIGABIT: $chunk\n"; } sub parse_ethernet { my $chunk = shift; $chunk =~ tr/\n/ /; print "ETHERNET: $chunk\n"; } __DATA__ ! interface Ethernet blah... blah... ! interface Gigabit blah... blah... ! interface Ethernet blah... !
    Anno
      For expanding Anno's solution, you can use a dispatch table to apply the corresponding policy :
      my %parsers = ( 'Ethernet' => sub { my $chunk = shift; $chunk =~ tr/\n/ /; return "ETHERNET: $chunk"; }, 'Gigabit' => sub { my $chunk = shift; $chunk =~ tr/\n/ /; return "GIGABIT: $chunk"; } ); $/ = "!\n"; $\ = "\n"; while ( <DATA> ) { chomp; if ( /^interface (.*)/ && $1 && exists $parsers{$1} ) { print $parsers{$1}( $_ ); } elsif ( /^system (.*)/ ) { # blah... } }
      Edit: As anonymous said, ( ) instead of { }. Copypasta from/to Firefox is treacherous.
        the statement

        my %parsers = { ... };

        seems wrong. should it not be

        my %parsers = ( ... );

Re: Need advice on how to break foreach parsing loop into functions
by quester (Vicar) on Sep 15, 2007 at 20:56 UTC
    If this is the output of a Cisco "show running-config" or "show startup-config", you might find it easier to have your script check the indentation. All the subcommands for an interface will be indented one space. The next command that isn't indented ends the stanza for that interface. For example, the description line for an interface will always match /^ description /.

    It doesn't necessarily work with configurations that have been edited for input to the router, though. On input, the router checks tables with the names of all the legal commands and subcommands, so it doesn't need to look at the indentation or at the "!" that it inserts when displaying configurations.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (4)
As of 2024-04-23 16:00 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found