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

I am trying to create a script that parses router interface stats and looks for error counters that are not equal to zero. Some counters may have large numbers like "Total output drops" (below). My regex is limited and I thought the following peice of code would work, but isn't. Also, would this approach be recommended for what I am trying to do?

Sample Text:
" Input queue: 0/75/0/39 (size/max/drops/flushes); Total output drops: 19845805"
if ($line =~ m/ Input queue: 0\/75\/(.*)\/39 (size\/max\/drops\/flush +es); Total output drops: (.*)/) { printf("%-20.20s %-22.22s : $line\n",$file,$currentInterface) if ($1 | +| $2 != 0); }

Thanks in advance!

Replies are listed 'Best First'.
Re: RegEx & Approach Question
by atemon (Chaplain) on Oct 09, 2007 at 14:07 UTC

    in your regex $2 is

    (size/max/drops/flushes).
    You need to escape the parenthesis. try the code after escaping (
    if ($line =~ m/\s+Input\s+queue:\s+0\/75\/(.*)\/39\s+\(size\/max\/drop +s\/flushes\); \s+Total\s+output drops:\s+(.*)/) { printf("%-20.20s %-22.22s : $line\n",$file,$currentInterface) if ($1 | +| $2 != 0); }
    Note: Since ( I think ) I found some mismatch in No. of spaces from your sample text with the regex, I replaced all spaces with \s+ in the regx.

    --VC



    There are three sides to any argument.....
    your side, my side and the right side.

      It's probably best to avoid hardcoding the other numbers.
      Also, much escaping could be avoided by using an alternate delimiter.

      m{\s+Input\s+queue:\s+\d+/\d+/(\d+)/\d+\s+\(size/max/drops/flushes\); +\s+Total\s+output drops:\s+(\d+)/
Re: RegEx & Approach Question
by toolic (Bishop) on Oct 09, 2007 at 14:25 UTC
    In addition to what vcTheGuru said, here are some other things to consider.

    Using an alternate delimiter for your regex would make it easier on the eye, and would avoid LTS (Leaning Toothpick Syndrome). Instead of "/", you could use "{}", for example:

    m{ Input queue: 0/75/(.*)/39 \(size/max/drops/flushes\); Total ou +tput drops: (.*)}

    Also consider making portions of the regex independent of whitespace. You could start the regex with m{\s+Input

    The $1 alone looks a little suspicous. If the regex matches, then $1 should be set, so checking it again seems redundant. Did you really mean if (($1 != 0) || ($2 != 0))

    If your captures should always be integers, it might be better to use (\d+) instead of (.*).

Re: RegEx & Approach Question
by mwah (Hermit) on Oct 09, 2007 at 14:18 UTC
    The following would speak if 0/75/0/39 (field 3) OR 19845805
    is non zero, "$a != 0 OR $b != 0":
    # ... printf "%-20.20s %-22.22s : $line\n", $file, $currentInterface if $line =~ m{e:\s+\d+/\d+/(\d+)/.+?:\s(\d+)$} && ($1 || $2); # ...
    I'm not sure what your condition really means. *When* exactly do you
    want to print the line?

    Regards

    mwa
Re: RegEx & Approach Question
by dwm042 (Priest) on Oct 09, 2007 at 14:45 UTC
    I've not always been happy with approaches that parse a whole line when the data I need was in part of the line.

    This is a partial line approach:

    #!/usr/bin/perl use warnings; use strict; my $file = "My file "; my $currentInterface = "Cisco Interface X"; while(<DATA>) { my $input_drops = 0; my $output_drops = 0; $input_drops = $1 if ( m{queue:\s+\d+/\d+/(\d+)/\d+} ); $output_drops = $1 if ( m{drops:\s+(\d+)$} ); printf("%-20.20s %-22.22s : $.\n",$file,$currentInterface) if ( $input_drops or $output_drops ); } __DATA__ Input queue: 0/75/0/39 (size/max/drops/flushes); Total output drops: +19845805
    And the output is:

    C:\Code>perl regex-test.pl My file Cisco Interface X : 1
Re: RegEx & Approach Question
by ewhitt (Scribe) on Oct 09, 2007 at 16:53 UTC
    Thanks everyone for your input. Your examples were enlightening. Here is the entire script if anyone has any further suggestions. Thanks again!
    ----Sample text---- P4-RTR-1>show interface ATM4/0/0 is up, line protocol is up Hardware is cyBus ENHANCED ATM PA, address is 00d0.03a9.33fc (bia 00 +d0.03a9.33fc) Description: (REMOVED) MTU 4470 bytes, sub MTU 4470, BW 44209 Kbit, DLY 1900 usec, reliability 255/255, txload 1/255, rxload 1/255 Encapsulation ATM, loopback not set Keepalive not supported Encapsulation(s): AAL5, PVC mode 4095 maximum active VCs, 1 current VCCs VC idle disconnect time: 300 seconds 7505 carrier transitions Last input 00:00:00, output 00:00:05, output hang never Last clearing of "show interface" counters never Input queue: 0/75/0/39 (size/max/drops/flushes); Total output drops: + 1984580 Queueing strategy: fifo Output queue: 0/40 (size/max) 5 minute input rate 2000 bits/sec, 2 packets/sec 5 minute output rate 1000 bits/sec, 2 packets/sec 6619976463 packets input, 4546821473409 bytes, 0 no buffer Received 0 broadcasts (9406304 IP multicasts) 0 runts, 0 giants, 0 throttles 1013 input errors, 1259 CRC, 0 frame, 0 overrun, 0 ignored, 0 abo +rt 4952767077 packets output, 2922582936024 bytes, 0 underruns 0 output errors, 0 collisions, 1 interface resets 0 output buffer failures, 0 output buffers swapped out ----Script---- #!/usr/bin/perl my $filesProcessed = 0; @files = <*.grab>; foreach $file (@files) { open(F, $file); my @import=<F>; close(F); my $currentInterface; foreach my $line(@import) { chomp($line); if ($line =~ m/ATM(.)\/(.)\/(.)/) { $currentInterface = "ATM$1/$2/$3"; } if ($line =~ m/Ethernet(.)\/(.)/) { $currentInterface = "Ethernet$1/$2"; } if ($line =~ m/FastEthernet(.)\/(.)/) { $currentInterface = "FastEthernet$1/$2"; } if ($line =~ m/GigabitEthernet(.)\/(.)/) { $currentInterface = "GigabitEthernet$1/$2"; } if ($line =~ m/10GigabitEthernet(.)\/(.)/) { $currentInterface = "10GigabitEthernet$1/$2"; } if ($line =~ m/Hssi(.)\/(.)/) { $currentInterface = "Hssi$1/$2"; } if ($line =~ m/Vlan(.)/) { $currentInterface = "Vlan$1"; } if ($line =~ m/ Input queue: (.*)\/(.*)\/(.*)\/(.*) \(size\/m +ax\/drops\/flushes\); Total output drops: (.*)/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($3 || $4 || $5); } if ($line =~ m/ (.) packets input, (.) bytes, (.) no buffe +r/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($3); } if ($line =~ m/ Received (.) broadcasts, (.) runts, (.) gi +ants, (.) throttles/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1 || $2 || $3); } if ($line =~ m/ (\d) input errors, (.) CRC, (.) frame, (.) + overrun, (.) ignored/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1 || $2 || $3 || $4 || $5); } if ($line =~ m/ (.) watchdog, (.) multicast, (.) pause inp +ut/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1 || $3); } if ($line =~ m/ (.) input packets with dribble condition d +etected/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1); } if ($line =~ m/ (.) packets output, (.) bytes, (.) underru +ns/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($3); } if ($line =~ m/ (.) output errors, (.) collisions, (.) int +erface resets/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1 || $2 || $3); } if ($line =~ m/ (.) babbles, (.) late collision, (.) defer +red/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1 || $2 || $3); } if ($line =~ m/ (.) lost carrier, (.) no carrier, (.) paus +e output/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1 || $2 || $3); } if ($line =~ m/ (.) output buffer failures, (.) output buf +fers swapped out/) { printf("%-30.30s %-22.22s $line\n",$file,$currentInterface +) if ($1 || $2); } } $filesProcessed++; } print "\nFiles Processed: $filesProcessed\n";
Re: RegEx & Approach Question
by snoopy (Curate) on Oct 10, 2007 at 23:31 UTC
    String::Scanf's format_to_re method might be useful for parsing these patterns. It lets you setup regular expressions in an sprintf like format.
    #!/usr/bin/perl use warnings; use strict; use String::Scanf qw(); sub scanf { my $pat = String::Scanf::format_to_re(shift); my $input = shift; return $input =~ /^${pat}$/; } my $format = "Input queue: %d/%d/%d/%d (size/max/drops/flushes); Total + output drops: %d"; my $input = "Input queue: 0/75/0/39 (size/max/drops/flushes); Total ou +tput drops: 19845805"; if (my ($size,$max,$drops,$flush,$total) = scanf($format, $input)) { warn "Gotcha: size=$size, max=$max,drops=$drops,flush=$flush,total=$ +total"; }