http://www.perlmonks.org?node_id=950822

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

Dear Monks, I am stuck with the following situation. I am sending a command to an equipment and I am getting the following output (multi line string). Please note that each line is indented as it is shown below:
MAC Address: xx:aa:aa:aa:aa:aa IP addr: a.b.c.d x.y.z.f Name: xxxx Neighbors attached: 1 Additional: xxx Interface: xxx Index: xxx Link Cost: xx Link Speed: xx Mbps MAC Address: xx:aa:aa:aa:aa:aa IP addr: a.b.c.d x.y.z.f Name: xxxx Neighbors attached: 1 Additional: xxx Interface: xxx Index: xxx Link Cost: xx Link Speed: xx Mbps
Each line that begins with ^MAC Address: is a node and I want to parse the lines so that relevant information relating to the node can be stored in a structure. e.g. In the above example, I have two nodes. Preferably, I am trying to have as output all information belonging to each node printed. e.g.
Node with MAC Address: XXX has IP = and Link Speed = and Link Cost = Node with MAC Address: YYY has IP = ...
Thanks in advance....

Replies are listed 'Best First'.
Re: regex multiple lines
by davido (Cardinal) on Jan 30, 2012 at 19:12 UTC

    This works for the data set provided. It is probably a little less efficient than a solution that breaks the problem into smaller chunks. But it is possible to do it all with one ugly regex. In this case it becomes easier to use named captures than to try to count $1, $2, etc. And the /x option helps to make it more readable.

    use strict; use warnings; my $string = <<HERE; MAC Address: xx:aa:aa:aa:aa:aa IP addr: a.b.c.d x.y.z.f Name: xxxx Neighbors attached: 1 Additional: xxx Interface: xxx Index: xxx Link Cost: xx Link Speed: xx Mbps MAC Address: xx:aa:aa:aa:aa:aa IP addr: a.b.c.d x.y.z.f Name: xxxx Neighbors attached: 1 Additional: xxx Interface: xxx Index: xxx Link Cost: xx Link Speed: xx Mbps HERE while( $string =~ m/ MAC\sAddress:\s(?<mac>\S+)\s+ IP\saddr:\s(?<ip1>\S+)\s+(?<ip2>\S+) .+? Link\sCost:\s(?<cost>\S+)\s+ Link\sSpeed:\s(?<speed>\S+)\sMbps /sxg ) { print "Node with MAC address: $+{mac} has IP = $+{ip1}, $+{ip2} " +. "and Link Speed = $+{speed} and Link Cost = $+{cost}\n"; }

    ...the output...

    Node with MAC address: xx:aa:aa:aa:aa:aa has IP = a.b.c.d, x.y.z.f and + Link Speed = xx and Link Cost = xx Node with MAC address: xx:aa:aa:aa:aa:aa has IP = a.b.c.d, x.y.z.f and + Link Speed = xx and Link Cost = xx

    Dave

Re: regex multiple lines
by JavaFan (Canon) on Jan 30, 2012 at 18:49 UTC
    Untested:
    $str = " ... "; # The data you posted my @data; foreach my $line (split /\n\s*/, $str) { if (/^MAC Address:\s*(\S+)/) { push @data, {mac_address => $1} } elsif (/^IP addr:\s*(\S+)/) { $data[-1]{ip_address} = $1; } elsif (/^Link Speed:\s*(\S+)/) { $data[-1]{link_speed} = $1; } elsif (/^Link Cost:\s*(\S+)/) { $data[-1]{link_cost} = $1; } } printf "Node with MAC Address: %s has IP = %s and Link Speed = %s and +Link Cost = %s\n", $_->{mac_address}, $_->{ip_address}, $_->{link_speed}, $_->{li +nk_cost} for @data;
      Thank you. your "untested" solution worked perfectly :)
Re: regex multiple lines
by choroba (Cardinal) on Jan 30, 2012 at 18:54 UTC
    Process the output line by line. Keep a hash of all the information read so far. On a MAC line, print the info if any, and clear the hash. On other lines, add info to the hash. After the loop, do not forget to print the last info.