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

I currently have a script that parses interfaces from a switch configuration.
Sample Text I Currently Parse: untagged Interface GigabitEthernet 1/2 untagged Interface GigabitEthernet 10/2
Code I use today: if ($parse =~ m/^untagged Interface GigabitEthernet (\d{1,2})\/(\d{1,2 +})/)/) { $slot = $1; $port = $2; ...Misc Code... }
Everything works fine, except now the CLI has changed slightly to better aggregate contiguous interfaces used (e.g. 1/3-5).
Sample New Text to Parse: untagged Interface GigabitEthernet 1/2,1/3-5,1/12,1/40
How can I extend my current approach to present the following output? I was thinking of using split with ","'s, but I can't figure out a way to identify the delta between "-"'s? Thanks in advance!
Output I need to make: untagged Interface GigabitEthernet 1/2 untagged Interface GigabitEthernet 1/3 untagged Interface GigabitEthernet 1/4 untagged Interface GigabitEthernet 1/5 untagged Interface GigabitEthernet 1/12 untagged Interface GigabitEthernet 1/40

Replies are listed 'Best First'.
Re: Switch CLI Parsing Question
by McDarren (Abbot) on Nov 19, 2007 at 11:41 UTC
    Not the most elegant solution, and probably a lot more verbose than it needs to be - but appears to give the desired result...
    #!/usr/bin/perl use warnings; use strict; while (my $line = <DATA>) { chomp($line); my @parts = split /\s/, $line, 4; for (split /,/, $parts[3]) { my ($slot, @ports) = split /\//; if ($ports[0] =~ /(\d+)-(\d+)/) { @ports = ($1 .. $2); } for my $port (0 .. $#ports) { print join(' ', @parts[0..2], "$slot/$ports[$port]\n"); } } } __DATA__ untagged Interface GigabitEthernet 1/2,1/3-5,1/12,1/40

    Output:

    untagged Interface GigabitEthernet 1/2 untagged Interface GigabitEthernet 1/3 untagged Interface GigabitEthernet 1/4 untagged Interface GigabitEthernet 1/5 untagged Interface GigabitEthernet 1/12 untagged Interface GigabitEthernet 1/40

    I'd suggest testing with more data before using it.
    Hope this helps,
    Darren :)

Re: Switch CLI Parsing Question
by sids (Acolyte) on Nov 19, 2007 at 14:56 UTC
    Although not the most efficient, this seems to do the job:
    while ($parse =~ s!(\d{1,2})/(\d{1,2})(?:-(\d{1,2}))?!!) { $end = $3 ? $3 : $2; print "untagged Interface GigabitEthernet $1/$_\n" for ($2 .. $end) }
    Test program:
    #!/usr/bin/perl use strict; use warnings; while (my $line = <DATA>) { chomp($line); while ($line =~ s!(\d{1,2})/(\d{1,2})(?:-(\d{1,2}))?!!) { my $slot = $1; my $port_start = $2; my $port_end = $3 ? $3 : $port_end; for my $port ($port_start .. $port_end) { print "untagged Interface GigabitEthernet $slot/$port\n"; } } } __DATA__ untagged Interface GigabitEthernet 1/2,1/3-5,1/12,1/40

    --
    Ignorance killed the cat, curiosity was framed.
Re: Switch CLI Parsing Question
by bobf (Monsignor) on Nov 20, 2007 at 03:10 UTC

    Here is one way to do it, inspired by number sequence.

    use strict; use warnings; my $in = '1/2,1/3-5,1/12,1/40'; my @munged = map { my ( $slot, $portstr ) = split( '/', $_ ); my @ports = ( $portstr =~ m/(\d+)-(\d+)/ ) ? $1 .. $2 : $ports +tr; map { join( '/', $slot, $_ ) } @ports; } split( ',', $in ); print "$_\n" for @munged;
    It could be shortened, of course, if length is an issue.

    You could also play with Set::IntSpan (as I mentioned in the aforementioned thread).

Re: Switch CLI Parsing Question
by locked_user sundialsvc4 (Abbot) on Nov 19, 2007 at 18:27 UTC

    Superficially looking at this problem, it seems to me that probably the best way to approach it is with a three-stage approach. First, you split the string by spaces into its four components. Then, you split the fourth component by commas. Finally, for-each element thus split, you are dealing with an easily-recognizable "a/b(-c?)" structure.

    In other words don't try to do it all at once.

    You also don't want to rely on things like $1 when each of those regular-expression operations will so-quickly redefine them. So, do things like:
    my ($a,$b,$c,$d) = split/\s+/,$myrec;
    where the tuple produced by "split" is immediately assigned to a corresponding tuple of discrete variables.

      Thank you everyone who has replied. The problem is that there would be more/less than four components split by commas. I am not sure if I was clear with that in my original posting. With that said, I assume I would be better off using an array than $a,$b,$c,$d. Am I correct in this assumption? Thanks!
Re: Switch CLI Parsing Question
by dcd (Scribe) on Nov 20, 2007 at 02:47 UTC
    Check out
    Set::IntRange - Sets of Integers
    This class lets you dynamically create sets of arbitrary intervals of integers and perform all the basic operations for sets on them (for a list of available methods and operators, see above).
    or
    Set::IntSpan - Manages sets of integers
    "Set::IntSpan" manages sets of integers. It is optimized for sets that have long runs of consecutive integers. These arise, for example, in .newsrc files, which maintain lists of articles:
    alt.foo: 1-21,28,31 alt.bar: 1-14192,14194,14196-14221
    Some of this is mentioned in
    Representing Sets in Perl