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

Parse variables from @output

by Cisco_Dave (Novice)
on Nov 14, 2019 at 20:34 UTC ( #11108699=perlquestion: print w/replies, xml ) Need Help??

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

Can someone help with this script please. I know it's probably easy but I'm more of a perl tweaker/Cisco man than a coder.

2nd and 3rd sections work fine creating the variables needed for later but the first section doesn't. @output is a grab of show ver command on a Cisco device, but in the sample code you can take this as sample outputs above.

# Get the line with the chassis on foreach (@output) { # (NX-OS devices - Chassis and memory) # Sample output # Hardware # cisco Nexus 3048 Chassis ("48x1GE + 4x10G Supervisor") # Intel(R) Celeron(R) CPU P450 with 3981680 kB of memory. if (/^Hardware/i) { $details{'Chassis'} = $1 if (/^\S+ cisco Nexus (\S+)/i); $details{'Main Memory'} = $5 if (/^\S+ Intel (\S+)/i); }

Here we would expect Chassis value to be 3048 and Main Memory value to be 3981680

# (IOS devices - Chassis and Main Memory) # Sample output # Cisco CISCO2901/K9 (revision 1.0) with 483328K/40960K bytes of memor +y. if (/^cisco ((Catalyst )?\S+) .* with (\S+) bytes of memory/i) { $details{'Chassis'} = $1; $details{'Main Memory'} = $3; }

Correct values of Chassis=CISCO2901/K9 and Main Memory=483328K obtained

# (WS-C3550-48's - Chassis) # Sample output # cisco WS-C3550-48 (PowerPC) processor (revision N0) with 65526K/8192 +K bytes of memory. # Model number: WS-C3550-48-SMI $details{'Chassis'} = $1 if (/^Model number: (\S+)/i); }

Correct value of Chassis=WS-C3550-48-SMI obtained.

Replies are listed 'Best First'.
Re: Parse variables from @output
by roboticus (Chancellor) on Nov 14, 2019 at 20:55 UTC

    Cisco_Dave:

    You don't specify what's wrong, so I can only guess: I'm thinking that line 15 should be:

    $details{'Main Memory'} = $1 if (/^\S+ Intel (\S+)/i);

    You have only one capture group (i.e., part of the regular expression inside parenthesis), so the captured value can only be in $1. For example:

    $ cat fluffy.pl use strict; use warnings; my $t = "Now is the time for all good men to come to the aid"; if ($t =~ /^(\w+) (\w+) (\w+) (\w+) (\w+) (\w+)/) { print "Words: 2='$2', 4='$4', 1='$1'\n"; } else { print "*** no match! ***\n"; }

    when you run it, you should get:

    $ perl fluffy.pl Words: 2='is', 4='time', 1='Now'

    In order to get better answers, it's helpful to include a bit more information. Especially if you can make a simple bit of code that can demonstrate the problem as I just did here. I hope I guessed correctly, but if not, update the code in your original question to make it clear. You could do so by putting something like:

    use strict; use warnings; my @output = ( "a few lines of data that I cut\n", "and pasted from a screen capture that I want to\n", "process.\n", );

    at the front, and something like:

    print "Chassis: <$details{'Chassis'}>\n"; print "Main Memory: <$details{'Main Memory'}>\n";

    at the end to show what you actually get. Then specify what you actually wanted instead, so we can clearly see the problem. Note the angle brackets I put in the output: whitespace handling errors (especially newlines and carriage returns) are so common that it's helpful to put something before and after the string so you can see if there are unwanted characters in your output.

    ...roboticus

    When your only tool is a hammer, all problems look like your thumb.

      Thanks for the quick reply. Will fully read and understand fully tomorrow. Have edited original post to help clarify a little.
Re: Parse variables from @output
by BillKSmith (Prior) on Nov 14, 2019 at 23:34 UTC
    You have provided your input (strangely called '@output') only as comments. We cannot tell exactly how this is broken into array elements or how those elements are broken into lines. Please post code that we can run and actually see the results that are correct and duplicate your problem. The following code should get you started. Please correct errors I may have made in understanding your data.
    use strict; use warnings; use Data::Dumper; my @output = ( qq/"Hardware\n/ .qq/"cisco Nexus 3048 Chassis ("48x1GE + 4x10G Supervisor")\n/ .qq/Intel(R) Celeron(R) CPU P450 with 3981680 kB of memory.\n/ , qq/Cisco CISCO2901\/K9 (revision 1.0)/ .qq/ with 483328K\/40960K bytes of memory.\n/ , qq/cisco WS-C3550-48 (PowerPC) processor (revision N0)/ .qq/ with 65526K\/8192K bytes of memory.\n/ .qq/Model number: WS-C3550-48-SMI\n/ ); foreach (@output) { my %details; if (/^Hardware/i) { $details{'Chassis'} = $1 if (/^cisco Nexus (\S+)/i); $details{'Main Memory'} = $1 if (/^Intel (\S+)/i); } if (/^cisco ((Catalyst )?\S+) .* with (\S+) bytes of memory/i) { $details{'Chassis'} = $1; $details{'Main Memory'} = $3; } $details{'Chassis'} = $1 if (/^Model number: (\S+)/i); print Dumper(\%details); }
    Bill

      Yes that's on the right lines Bill thanks. Will test again tomorrow.

      Essentially in this example there are three different contents for @output depending on the type of device the show ver command is run on. Each if statement determines which of the three it is, i.e. if there is a regex match for the line it uses that version of @output and sets the variables for chassis and memory from the output there

        Still struggling, update in main thread :(
Re: Parse variables from @output
by NetWallah (Canon) on Nov 14, 2019 at 23:48 UTC
    See if this extendable (declarative) coding style works for you:
    use strict; use warnings; my %details; my @rules=( "Hardware" => sub{$details{HARDWARE_SEEN} = 1 }, 'cisco Nexus (\S+)' => sub{return 0 unless $details{HARDWARE_SEEN}; $details{Chassis} = $_[0]; }, 'Intel.+?(\d+)\s*(\w{2}) of memory' => sub{ return 0 unless $details{HARDWARE_SEEN} +; $details{"Main Memory"} = $_[0]; $details{"Main Memory Unit"} = $_[1]; }, ); while (<DATA>){ for (my $i=0; $i < $#rules; $i+=2){ my $regex=qr|$rules[$i]|; my $action = $rules[$i+1]; next unless m/$regex/i; last if $action->($1,$2,$3); } } for (sort keys %details){ print "$_\t = $details{$_}\n"; } __DATA__ # Sample output # Hardware # cisco Nexus 3048 Chassis ("48x1GE + 4x10G Supervisor") # Intel(R) Celeron(R) CPU P450 with 3981680 kB of memory.
    Output:
    Chassis = 3048 HARDWARE_SEEN = 1 Main Memory = 3981680 Main Memory Unit = kB
    Feel free to post questions about the code here.

                    "From there to here, from here to there, funny things are everywhere." -- Dr. Seuss

      Looks impressive, appreciate this but I'm sure I can get it working with just a small tweak ;)

Re: Parse variables from @output
by Cisco_Dave (Novice) on Nov 15, 2019 at 08:13 UTC
    #!/usr/local/bin/perl ## use strict; use warnings; use Data::Dumper; my @output = ( qq/Hardware\n/ .qq/" cisco Nexus 3048 Chassis ("48x1GE + 4x10G Supervisor)"\n/ .qq/" Intel(R) Celeron(R) CPU P450 with 3981680 kB of memory."\n/ , qq/"Cisco CISCO2901\/K9 (revision 1.0) with 483328K\/40960K bytes + of memory."\n/ , qq/"cisco WS-C3550-48 (PowerPC) processor (revision N0) with 6552 +6K\/8192K bytes of memory."\n/ .qq/"Model number: WS-C3550-48-SMI"\n/ ); foreach (@output) { my %details; if (/^Hardware/i) { $details{'Chassis'} = $1 if (/^cisco Nexus (\S+)/i); $details{'Main Memory'} = $1 if (/^Intel (\S+)/i); } if (/^cisco ((Catalyst )?\S+) .* with (\S+) bytes of memory/i) { $details{'Chassis'} = $1; $details{'Main Memory'} = $3; } $details{'Chassis'} = $1 if (/^Model number: (\S+)/i); print Dumper(\%details); }

    Result...

    $VAR1 = {}; $VAR1 = {}; $VAR1 = {};

    Result needs to be...

    $VAR1 = { 'Main Memory' => '3981680', 'Chassis' => '3048' }; $VAR1 = { 'Main Memory' => '483328K/40960K', 'Chassis' => 'CISCO2901/K9' }; $VAR1 = { 'Main Memory' => '65526K/8192K', 'Chassis' => 'WS-C3550-48' };
Re: Parse variables from @output
by Cisco_Dave (Novice) on Nov 15, 2019 at 08:40 UTC

    Removed the quotation marks to fix second and third values for $VAR1. Just need to figure out the first result now, note the two leading spaces are intentional below, this is how it appears in the output

    #!/usr/local/bin/perl ## use strict; use warnings; use Data::Dumper; my @output = ( qq/Hardware\n/ .qq/ cisco Nexus 3048 Chassis ("48x1GE + 4x10G Supervisor)\n/ .qq/ Intel(R) Celeron(R) CPU P450 with 3981680 kB of memory.\n/ , qq/Cisco CISCO2901\/K9 (revision 1.0) with 483328K\/40960K bytes +of memory.\n/ , qq/cisco WS-C3550-48 (PowerPC) processor (revision N0) with 65526 +K\/8192K bytes of memory.\n/ .qq/Model number: WS-C3550-48-SMI\n/ ); print "@output\n"; foreach (@output) { my %details; if (/^Hardware/i) { $details{'Chassis'} = $1 if (/^cisco Nexus (\S+)/i); $details{'Main Memory'} = $1 if (/^Intel (\S+)/i); } if (/^cisco ((Catalyst )?\S+) .* with (\S+) bytes of memory/i) { $details{'Chassis'} = $1; $details{'Main Memory'} = $3; } $details{'Chassis'} = $1 if (/^Model number: (\S+)/i); print Dumper(\%details); }

    Result...

    Hardware cisco Nexus 3048 Chassis ("48x1GE + 4x10G Supervisor) Intel(R) Celeron(R) CPU P450 with 3981680 kB of memory. Cisco CISCO2901/K9 (revision 1.0) with 483328K/40960K bytes of memory +. cisco WS-C3550-48 (PowerPC) processor (revision N0) with 65526K/8192K + bytes of memory. Model number: WS-C3550-48-SMI $VAR1 = {}; $VAR1 = { 'Main Memory' => '483328K/40960K', 'Chassis' => 'CISCO2901/K9' }; $VAR1 = { 'Main Memory' => '65526K/8192K', 'Chassis' => 'WS-C3550-48' };

      You have:

      .qq/ cisco Nexus 3048 Chassis ("48x1GE + 4x10G Supervisor)\n/

      but your match wants cisco to appear at the start of a line:

      $details{'Chassis'} = $1 if (/^cisco Nexus (\S+)/i);

      You will need to allow for some whitespace in front of cisco, for example by allowing for \s*:

      $details{'Chassis'} = $1 if (/^\s*cisco Nexus (\S+)/i);

      But "start of the line" (^) means "start of the whole string" to Perl unless you also tell it to match after a newline using the /m modifier:

      $details{'Chassis'} = $1 if (/^\s*cisco Nexus (\S+)/mi);

      Adding that, I get:

      $VAR1 = { 'Chassis' => '3048' }; $VAR1 = { 'Main Memory' => '483328K/40960K', 'Chassis' => 'CISCO2901/K9' }; $VAR1 = { 'Chassis' => 'WS-C3550-48', 'Main Memory' => '65526K/8192K' };

        That's spot on thanks. Chassis variable working. Any idea on the memory part?

        $details{'Main Memory'} = $1 if (/^\s* Intel .* with (\S+) kB of memor +y/mi);
Re: Parse variables from @output
by Cisco_Dave (Novice) on Nov 15, 2019 at 12:18 UTC
    Have expanded the variables further to fix more issues now.
    #!/usr/local/bin/perl ## use strict; use warnings; use Data::Dumper; my @output = ( qq/Software\n/ .qq/ BIOS: version 3.6.0\n/ .qq/ kickstart: version 7.1(5)N1(1)\n/ .qq/ system: version 7.1(5)N1(1)\n/ .qq/ kickstart image file is: bootflash:\/\/\/n5000-uk9-kickstart +.7.1.5.N1.1.bin\n/ .qq/ system image file is: bootflash:\/\/\/n5000-uk9.7.1.5.N1. +1.bin\n/ .qq/Hardware\n/ .qq/ cisco Nexus 5548 Chassis ("O2 32X10GE\/Modular Universal Pla +tform Supervisor")\n/ .qq/ Intel(R) Xeon(R) CPU with 8253840 kB of memory.\n/ .qq/ Processor Board ID FOC163849D1\n/ .qq/ Device name: tpecasw01\n/ .qq/ bootflash: 2007040 kB\n/ .qq/Kernel uptime is 636 day(s), 1 hour(s), 34 minute(s), 49 secon +d(s)\n/ ); print "@output\n"; foreach (@output) { my %details; if (/^Hardware/mi) { $details{'Chassis'} = $1 if (/^\s*cisco Nexus (\S+) Chassi +s/mi); $details{'Main Memory'} = $1 if (/^\s*Intel.* with (\S+) kB of + memory/mi); $details{'Name'} = $1 if (/^\s*Device name: (\S+)/mi); $details{'Uptime'} = $1 if (/^\s*Kernel uptime is (.*?)\s*$/mi +); $details{'Version'} = $1 if (/^\s*system: .*version (\S+?)[,\s +]/mi); $details{'IOS File'} = $1 if (/^\s*system image file is:\s*"(. +*)"/mi); $details{Serial} = $1 if (/^\s*Processor board ID (\S+)/mi); $details{'ROM Version'} = $1 if (/^\s*BIOS: .*version (\S+?)[, +\s]/mi); # aka BIOS $details{'Boot Version'} = $1 if (/^\s*kickstart: .*version (\ +S+?)[,\s]/mi); # aka kickstarter $details{'Boot Flash'} = $1 if (/^\s*bootflash: (\S+)/mi); } print Dumper(\%details); }

    All are working apart from $details('IOS File') is not populating/displaying. Any ideas? Result should be bootflash:///n5000-uk9.7.1.5.N1.1.bin

    Value for 'IOS File' missing in results...

    Software BIOS: version 3.6.0 kickstart: version 7.1(5)N1(1) system: version 7.1(5)N1(1) kickstart image file is: bootflash:///n5000-uk9-kickstart.7.1.5.N1.1 +.bin system image file is: bootflash:///n5000-uk9.7.1.5.N1.1.bin Hardware cisco Nexus 5548 Chassis ("O2 32X10GE/Modular Universal Platform Sup +ervisor") Intel(R) Xeon(R) CPU with 8253840 kB of memory. Processor Board ID FOC163849D1 Device name: tpecasw01 bootflash: 2007040 kB Kernel uptime is 636 day(s), 1 hour(s), 34 minute(s), 49 second(s) $VAR1 = { 'Uptime' => '636 day(s), 1 hour(s), 34 minute(s), 49 second( +s)', 'Serial' => 'FOC163849D1', 'Version' => '7.1(5)N1(1)', 'ROM Version' => '3.6.0', 'Main Memory' => '8253840', 'Name' => 'tpecasw01', 'Boot Version' => '7.1(5)N1(1)', 'Chassis' => '5548' };

      Again, compare your regular expression against the string you're trying to match, and eliminate the differences:

      system image file is: bootflash:\/\/\/n5000-uk9.7.1.5.N1.1.bin +\n /^\s*system image file is:\s*"(.*)"/mi

      Maybe you want to use a more interactive tool to find where your regex fails, for example haukex's interactive Perl regex tester.

        Already tried that one, doesn't work I'm afraid

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others cooling their heels in the Monastery: (4)
As of 2020-12-04 14:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you use taint mode?





    Results (60 votes). Check out past polls.

    Notices?