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

Fellow monks, I ask again for your help. This one's got me completely stumped...

Following is my code, verbatim:

local $network_ifs = `netstat -i | grep -Ev "lo|sit|link|Name" | cut - +f1 -d" " | uniq | sort -n`; foreach (split("\n", $network_ifs)) { push @tcp_utilization_metrics, "$_"; } %tcp_utilization_adapter_names = (); foreach $network_if (@tcp_utilization_metrics) { local $if_name = $network_if; if ($network_if =~ /en/) { $network_if =~ substr ($network_if, 2, 0, "t"); } if ($network_if =~ /tr/) { $network_if =~ substr ($network_if, 1, 2, "ok"); } if ($network_if =~ /at/) { $network_if =~ substr ($network_if, 2, 0, "m"); } print "$if_name: $network_if\n"; $tcp_utilization_adapter_names{"$if_name"} = "$network_if"; print "$tcp_utilization_adapter_names{$if_name}\n"; } foreach (@tcp_utilization_metrics) { print "$tcp_utilization_adapter_names{$_}\n"; }

And following is the program's applicable output (with empty lines indicated):

en0: ent0   
ent0        
en1: ent1   
ent1        
(empty line)
(empty line)

I've been over and through this code, and I just can't figure out what's wrong. Can anyone else see it? Many thanks and much appreciation!

Replies are listed 'Best First'.
Re: Simple hash assignment...or is it?
by delirium (Chaplain) on Nov 22, 2003 at 01:35 UTC
    One of Perl's cooler selling points is being able to substitute for programs like cut, grep, uniq, and sort by using native Perl functions.

    I've rewritten what you have a little (the output is different, but I think I get the gist of what you're trying to do) so that the only shell is for the netstat command.

    #!/usr/bin/perl use strict; my %adapter_names = (); for (split /\n/, `netstat -i`) { next if /lo|sit|link|Name/; /(\S+)/; # Takes first block of non-spaces, assigns to $1 my $interface = $1; my $adapter = $interface; $adapter =~ s/^en/ent/; # Add other substitutions here $adapter_names{$adapter} = $interface if !defined $adapter_names{$ad +apter}; } print "$_ : $adapter_names{$_}\n" for sort keys %adapter_names;
      Yeah...Perl is really cool. As I said before (see above) I come from a functional/procedural background, so that's what my code tends to look like. (Personally, I like parenthesizing... :)

      This code is very nice, very clean. I wonder what the speed difference is? I may adopt this style in a later revision (although possibly still pre-production) because the program is part of a stress level monitoring toolkit, with the obvious desire to keep the induced stress as low as possible. So, if I can exec 1 program instead of 5, I lower the induced stress quite a bit.

Re: Simple hash assignment...or is it?
by mpeppler (Vicar) on Nov 22, 2003 at 01:02 UTC
    Various things...

    First you're using local where I think you mean my. Second, you've got your hash assignment backwards. If you want

    foreach (@tcp_utilization_metrics) { print "$tcp_utilization_adapter_names{$_}\n"; }
    to actually print something, then you need to change
    $tcp_utilization_adapter_names{"$if_name"} = "$network_if";
    to
    $tcp_utilization_adapter_names{$network_if} = $if_name;
    Michael
      I'm keying my hash on $if_name, not $network_if, because $if_name should be equivalent to one member of @tcp_utilization_metrics, which I use to loop through each value in the hash. Please notice that $if_name is assigned the same value as $network_if at the top of the loop, but $network_if is the variable whose value is changed during the loop.
        Rats - read it backwards :-(

        Well - I just ran your code here (on linux), adding "use strict", and adding "my" as appropriate, and it ran correctly (or at least I don't have any empty lines anywhere).

        Ah - I know what the problem is:

        foreach $network_if (@tcp_utilization_metrics) { local $if_name = $network_if; if ($network_if =~ /en/) { $network_if =~ substr ($network_if, 2, 0, "t"); }
        When change the $network_if value here you actually modify the @tcp_utilization_metrics array - see the docs on the behavior of the foreach loop.

        If you reverse things you should be fine:

        foreach $if_name (@tcp_utilization_metrics) { local $network_if = $if_name; if ($network_if =~ /en/) { $network_if =~ substr ($network_if, 2, 0, "t"); }
        That said - you should "use strict" and replace those "local" with "my".

        Michael

Re: Simple hash assignment...or is it?
by Roger (Parson) on Nov 22, 2003 at 00:55 UTC
    You can use the Data::Dumper module to investigate your hash and array structures.
    use Data::Dumper; ... # insert after the first foreach loop. print Dumper(\@tcp_utilization_metrics);
      The Dumper output shows me the same thing as my own print statements: that there's some difference between the interface names I'm passing into the foreach block, and what's getting set as hash keys. It's a very confusing problem:
      $VAR1 = (                 
                'en0',          
                'en1'           
              );                
      en0: ent0                 
      ent0                      
      en1: ent1                 
      ent1                      
      $VAR1 = {                 
                'en0' => 'ent0',
                'en1' => 'ent1' 
              };                
      (empty line)
      (empty line)

      because, as you can see, the hash keys are the same string as the members of the array.

        I have rewritten your code to make it a bit Perl like. The following code is equivalent to your code -
        use strict; use Data::Dumper; my $network_ifs = `netstat -i | grep -Ev "lo|sit|link|Name" | cut -f1 +-d" " | uniq | sort -n`; my @tcp_utilization_metrics = split '\n', $network_ifs; my %tcp_utilization_adapter_names; foreach my $network_if (@tcp_utilization_metrics) { next if ! $network_if; # ignore empty lines if any my $if_name = $network_if; if ($network_if =~ /en/) { substr($network_if, 2, 0) = "t"; # insert 't' } elsif ($network_if =~ /tr/) { substr($network_if, 1, 2) = "ok"; # replace 'ok' } elsif ($network_if =~ /at/) { substr($network_if, 2, 0) = "m"; # insert 'm' } # insert hash entries $tcp_utilization_adapter_names{$if_name} = $network_if; } # investigate the hash foreach (keys %tcp_utilization_adapter_names) { print "$tcp_utilization_adapter_names{$_}\n"; }
        Perhaps you could tell us what is your expected hash output. I highly suspect that you want the following instead in your code (swap the key-value in the hash) -
        # insert hash entries $tcp_utilization_adapter_names{$network_if} = $if_name;
Re: Simple hash assignment...or is it?
by delirium (Chaplain) on Nov 22, 2003 at 00:54 UTC
    How about a description of what you are doing and what the output should look like?
      I'm getting AIX network interface names and transforming them into the corresponding network adapter names. I'm using the interface names as keys, and the adapter names as values in the hash.
Re: Simple hash assignment...or is it?
by arootbeer (Novice) on Nov 22, 2003 at 06:29 UTC
    Wow...I can't believe I spaced on this code. I was telling a friend about it, and he kind of looked at me blankly, like "what did you think it was going to do?" This is a case of too long a day... :)

    I think I honestly thought that it was giving me the wrong output because the output wasn't what I wanted it to be...

    Thanks for all the help to everyone who pitched in to help clear the room of my brain fart!

      It appears that you are attempting to collect protocol statistics for network adapters.

      If this is the case, there are several readymade, free tools that are more suited to that task, and can perform it far more efficiently, and in "standard" manner, with oh-so-many more features.

      Most of these tools involve SNMP, and if you have not seen MRTG yet, check out http://mrtg.org

        Well, in my current assignment, I've been asked to do it using only AIX command line calls. Don't ask...

        Personally, I was going to use PTX, which is a VERY fully featured AIX Performance monitoring and reporting solution, but this is the way I was asked to do it, so this is the way it must be...

        I will check out the site you mentioned; thanks for the heads-up!