Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight

Puzzling Hash of Arrays problem

by Tuna (Friar)
on Apr 21, 2001 at 01:58 UTC ( #74345=perlquestion: print w/replies, xml ) Need Help??
Tuna has asked for the wisdom of the Perl Monks concerning the following question:

I have a program which, among other things, reads archived files containg the output of "show diagbus" on Cisco routers. The relevant strings I'm matching in my regex are:

L3 Engine: 0 - OC12 (622 Mbps)
L3 Engine: 1 - OC12 (622 Mbps)
L3 Engine: 2 - OC12 (622 Mbps)
L3 Engine: 4 - OC12 (622 Mbps)

Here's a code snippet:
%router_of_interest = (router1 => "1", router2 => "1", router3 => "1", router4 => "1" ); while ($file = readdir(DIA)) { if ($file =~ /(.*)\ { $router = $1; next unless exists $router_of_interest{$router}; open(DIAFILE,"$dir/$file") || die "Cannot open $dir/$file: $!\n"; while ($line = <DIA>) { chomp $line; if ($line =~ /^\s*L3 Engine: (\d+)/i){ $engine = $1; if (grep(/$engine/,@{$HoL_engine{$router}}) != 1){ push @{$HoL_engine{$router}}, $engine; } } } if (exists $HoL_engine{$router}){ $engine{$router} = join(",",sort @{$HoL_engine{$router}}); } else { $engine{$router} = "NA"; #GSR IOS version too old } } } foreach $key (sort keys %engine) { print "$key - Engine $engine{$key}\n"; }
this successfully prints:

router1 - Engine 0,2,
router2 - Engine 1,2
router3 - Engine 2
router4 - Engine NA


Except, it won't print instances of:

router1 - Engine 0

Something about pushing one "0" and nothing else into my HoL fails. Can anyone help a poor Monk out?

Replies are listed 'Best First'.
Re: Puzzling Hash of Arrays problem
by athomason (Curate) on Apr 21, 2001 at 03:10 UTC
    Hmmm... once I puzzled my way through your unusual indentation, I noticed a few things that would make this better code.
    • You don't declare any of your variables. Get into the habit of doing that by always using the strict pragma. Also turn on warnings, since that will frequently catch the $self->bonk() type problems that would otherwise leave you scratching your head for a while.

    • This code: if (grep(/$engine/,@{$HoL_engine{$router}}) != 1) screams "hash!" to me. You're trying to keep track of which "engines" appear in each "router", right? Hashes do all the work of checking for existing values for you, like this:$HoH_engine{$router}{$engine} = 1;
      instead of
      if (grep(/$engine/,@{$HoL_engine{$router}}) != 1){ push @{$HoL_engine{$router}}, $engine; }

      Avoiding grep has the handy side effect of skirting the problem of 0 being false, which cLive points out above. Alternatively, you could use an array if you knew the values would all be small integers, like this: $HoL_engine{$router}[$engine] = 1;. That would require that you get the list out at the end with $engine{$router} = join(",", grep {$_} @{$HoL_engine[$router]});. Notice that this won't have the same truth problems as your initial form.

    • It looks like you're reading from the dirhandle <DIA> instead of the filehandle <DIAFILE> that you just opened. I'd be surprised if your code would work at all with that, so I'm guessing that's a typo.

    After all that, we're left without something like this, which worked for me on manufactured data.

    use strict; use warnings; my %router_of_interest = ( router1 => 1, router2 => 1 ); # .... my %engine; opendir DIA, '/'; while (my $file = readdir(DIA)) { next unless $file =~ /(.*)\; my $router = $1; next unless exists $router_of_interest{$router}; my %HoH_engine; open(DIAFILE, "<$file") or die "Cannot open $file: $!\n"; while (my $line = <DIAFILE>) { chomp $line; if ($line =~ /^\s*L3 Engine: (\d+)/i) { my $engine = $1; $HoH_engine{$router}{$engine} = 1; } } if (exists $HoH_engine{$router}){ $engine{$router} = join(",", sort keys %{$HoH_engine{$router}}); } else { $engine{$router} = "NA"; #GSR IOS version too old } } foreach my $key (sort keys %engine) { print "$key - Engine $engine{$key}\n"; }

    Update: Still broken? Odd... when I put L3 Engine: 0 - OC12 (622 Mbps) by itself in, I get the outputrouter1 - Engine 0. Could something be wrong elsewhere?

      Thanks for the reply. For the sake of brevity, I omitted parts of the program, including the section where I declare my variables. Thanks to all here, I ALWAYS use strict and -w! =)

      However, your suggestions produce the exact same results as my code. ++, nonetheless!

      update: typo

      update 2: Well, I cut 'n pasted again, and lo' and behold it works!


Re: Puzzling Hash of Arrays problem
by jeroenes (Priest) on Apr 21, 2001 at 02:35 UTC
    Maybe it is in the grep line:
    if (grep(/$engine/,@{$HoL_engine{$router}}) != 1){
    Try: if ( index( join( '', @{$HoL_engine{$router}}), $engine)<0 ){
    I always have trouble looking into the actions of grep. Rest of the code seems OK to me...

    Hope this helps,

    "We are not alone"(FZ)

Re: Puzzling Hash of Arrays problem
by cLive ;-) (Prior) on Apr 21, 2001 at 02:35 UTC
    how about changing assignment to:
    $engine = sprintf(%02d,$1);

    to make "00" which is true, rather than "0" which isn't, in cases where $1 = 0.

    Seekers of truth must know thy path...

    what is truth? (Camel 2, page 20)

    • any string is true except for "" and "0"
    • any number is true except for 0
    • any reference is true
    • any undefined value is false

    So as long as you test "00" in a string rather than numerical context, you should be OK.

    cLive ;-)

    Update: after a minute's thought, I changed the line to sprintf to the assignment...

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://74345]
Approved by root
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (5)
As of 2017-11-22 02:15 GMT
Find Nodes?
    Voting Booth?
    In order to be able to say "I know Perl", you must have:

    Results (314 votes). Check out past polls.