in reply to scan, match and extract

Here is an SSCCE showing the storage of the capture groups in an array. Then you don't need a list of numbered scalars.

use strict; use warnings; use Test::More tests => 2; my $re_dn = qr{^(\w+) (\d+) (\d{2}:\d{2}:\d{2}) .*: eth(\d): link down +}; my $re_up = qr{^(\w+) (\d+) (\d{2}:\d{2}:\d{2}) .*: eth(\d): link up, +(\d+)Mbps, ([^,]+), lpa (\w+)}; my @log = ( 'May 19 13:58:01 foo: eth0: link down', 'May 19 13:58:11 foo: eth0: link up, 100Mbps, bar, lpa quux' ); for (@log) { my @matches; if (@matches = /$re_dn/) { my ( $mon, $day, $time, $interface ) = @matches; is $time, '13:58:01'; next; } if (@matches = /$re_up/) { my ( $mon, $day, $time, $interface, $rate, $duplex, $lpa ) = @ +matches; is $time, '13:58:11'; next; } }

Increase the number of tests as you wish to confirm that all the groups are returning the correct results.


🦛

Replies are listed 'Best First'.
Re^2: scan, match and extract
by Marshall (Canon) on May 20, 2022 at 05:30 UTC
    When coding like this, I usually omit @matches and proceed directly with assigning to the scalars in list context. I think the following code is equivalent. I guess this is just a fine style point. In any event, the need to explicitly reference $1,$2,$3,etc. is rare.

    use strict; use warnings; use Test::More tests => 2; my $re_dn = qr{^(\w+) (\d+) (\d{2}:\d{2}:\d{2}) .*: eth(\d): link down +}; my $re_up = qr{^(\w+) (\d+) (\d{2}:\d{2}:\d{2}) .*: eth(\d): link up, +(\d+)Mbps, ([^,]+), lpa (\w+)}; my @log = ( 'May 19 13:58:01 foo: eth0: link down', 'May 19 13:58:11 foo: eth0: link up, 100Mbps, bar, lpa quux' ); for (@log) { if (my ( $mon, $day, $time, $interface ) = /$re_dn/) { is $time, '13:58:01'; next; } if (my ( $mon, $day, $time, $interface, $rate, $duplex, $lpa ) = / +$re_up/) { is $time, '13:58:11'; next; } }
    Update:
    It could be that something other than a "one step" regex might be appropriate?. It appears that ": link down" or ": link up" are key things, and I am guessing that that there a heck of a lot of lines in this file that don't have those key words. It could be that if speed is important, do a strcmp() index() looking for either of those phrases on the line, or just ": link". If the line contains one of those strings, it could also be that a simple split on whitespace+,[\s;,] would be faster than a regex. Or at least easier to write.

    I parse a lot of printouts that are just intended for humans to read. There is kind of an "art" to that. And the result is always inherently unstable because there is no defined spec. This looks to be a lot more constrained because this appears to be some kind of standard program generated log.

    Update 2:
    In my opinion, the names $re_dn and $re_up are one step "too clever". I probably would have used $re_link_down and $re_link_up. Using some extra characters in the name costs essentially nothing in compile speed. And I guess if you have a really fancy editor or IDE with auto complete, nothing much in typing either. This is a style issue. I mention it because details like this can matter when you re-visit code that you wrote X years ago.

    use strict; use warnings; my @log = ( 'May 18 12:01:01 bar: eht1 BS', 'May 19 13:58:01 foo: eth0: link down', 'May 19 13:58:11 foo: eth0: link up, 100Mbps, bar, lpa quux' ); foreach my $line (@log) { next unless (index($line, ": link")>=0); # skips BS # this is very fast # even running twice for # link up or link down is +fast print "$line\n"; # focus on link mentioning lines here } #May 19 13:58:01 foo: eth0: link down #May 19 13:58:11 foo: eth0: link up, 100Mbps, bar, lpa quux