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

Hi Monks!
I've written a script using Net::Telnet::Cisco to log into an access point and capture information about associated clients. I simply output this to a log file and then parse the file. What I'd like to do is take only the values MAC address, Signal Strength, and Signal-to-Noise Ratio from the output for each client. What I will do is load the output file and parse each line with regex to pull out the relevant data. However, not knowing how many clients will be associated at any one time, how can I create an associative array for each client that will hold the relevant data? For example, can I create arrays named '%client_i' where the value of i simply increments from 0 to the number of clients returned? A new client will be identified each time a MAC address is found while parsing the output log. Secondly, can I use each of these '%client_i' arrays to store key/value pairs such as
%client_0 = ("MAC",$mac,"RSSI",$rssi,"SNR",$snr);

Thanks in advance.

Replies are listed 'Best First'.
Re: Create Arrays On-the-Fly
by fullermd (Vicar) on May 27, 2009 at 17:06 UTC

    You can have an array of hash refs instead.

    @clients = ( { MAC => $mac, RSSI => $rssi, SNR => $snr }, /* 0 */ { MAC => $secondmac, RSSI => $secondrssi, SNR => $secondsnr }, /* +1 */ /* etc */ );

    Then you can grab the bits like

    %client_0 = %{$clients[0]};

    etc.

    To build it on the fly, you'd have code like:

    my @clients; # The whole hash at once $clients[0] = { MAC => $mac, /* ... */ }; # Or setting individual bits $clients[0]->{MAC} = $mac;

    This is pretty well covered in the documentation, such as in perlreftut and perldsc.

      fullermd -

      My issue here is with creating the array and populating it not knowing how many clients I'll have at any one time. So I guess what I'll have to do is output the command to show the currently connected clients to a text file and parse the text file until I reach EOF. Since the file contains words and information other than MAC addresses, I'll know that when I regex match a MAC address, that will be a line to parse and add to the array. Again, I do this until I reach EOF.

      Thanks!!

        Of course, if you're always looking up data by MAC, it could make more sense to structure it as a giant hash, keyed by the MAC, of hash (refs) of the data. That would make looking up particular MAC's faster and a bit cleaner. It may be easier to read too, depending on your own preferences.

        #!/usr/bin/env perl5 use strict; use warnings; my %clients; my $curmac; while(<DATA>) { chomp; # Assume a format of "Type Data" my ($type, $data) = split / /; # If it's a MAC address, assume later lines refer to this MAC if($type eq "MAC") { $curmac = $data; next; } # RSSI and SNR just get saved $clients{$curmac}{RSSI} = $data if $type eq "RSSI"; $clients{$curmac}{SNR} = $data if $type eq "SNR"; } # Show it use Data::Dumper; print Dumper \%clients; # Find a given entry sub showmacsnr { my $mac = shift; my $ent = $clients{$mac}; if($ent) { print "MAC $mac has SNR $ent->{SNR}\n"; } else { print "Can't find MAC $mac\n"; } } showmacsnr("ba:98:76:54:32:10"); showmacsnr("thi:is:is:nt:re:al"); # Sample data __DATA__ MAC 01:23:45:67:89:ab RSSI 12 SNR 18 MAC ba:98:76:54:32:10 RSSI 7 SNR 3
        % ./tst.pl $VAR1 = { 'ba:98:76:54:32:10' => { 'SNR' => '3', 'RSSI' => '7' }, '01:23:45:67:89:ab' => { 'SNR' => '18', 'RSSI' => '12' } }; MAC ba:98:76:54:32:10 has SNR 3 Can't find MAC thi:is:is:nt:re:al

        Right, but you don't have to presize it. Just push entries onto it as you parse them.

        For instance, to invent a stupid-simple data format, and demonstrate parsing it and doing a thing or two with the data once parsed:

        #!/usr/bin/env perl5 use strict; use warnings; my @clients; my %tmp; while(<DATA>) { chomp; # Assume a format of "Type Data" my ($type, $data) = split / /; # If it's a MAC address, save off our pre-existing record (if any) +, # and start a new one. if($type eq "MAC") { if(keys %tmp) { # Have to unroll a copy here, otherwise the next line empt +ies # it out. push @clients, { (%tmp) }; %tmp = (); } $tmp{MAC} = $data; } # RSSI and SNR just get saved $tmp{RSSI} = $data if $type eq "RSSI"; $tmp{SNR} = $data if $type eq "SNR"; } # Save the 'last' one if we fell off the end push @clients, \%tmp if keys %tmp; # Show it use Data::Dumper; print Dumper \@clients; # Find a given entry sub showmacsnr { my $mac = shift; my @ba = grep { $_->{MAC} eq $mac } @clients; if(@ba) { my $ent = $ba[0]; print "MAC $mac has SNR $ent->{SNR}\n"; } else { print "Can't find MAC $mac\n"; } } showmacsnr("ba:98:76:54:32:10"); showmacsnr("thi:is:is:nt:re:al"); # Sample data __DATA__ MAC 01:23:45:67:89:ab RSSI 12 SNR 18 MAC ba:98:76:54:32:10 RSSI 7 SNR 3

        So you end up with output like:

        % ./tst.pl $VAR1 = [ { 'RSSI' => '12', 'SNR' => '18', 'MAC' => '01:23:45:67:89:ab' }, { 'SNR' => '3', 'RSSI' => '7', 'MAC' => 'ba:98:76:54:32:10' } ]; MAC ba:98:76:54:32:10 has SNR 3 Can't find MAC thi:is:is:nt:re:al
Re: Create Arrays On-the-Fly
by roubi (Hermit) on May 27, 2009 at 17:03 UTC
    You probably should use an array of hashes, as such:
    my @clients; $clients[0] = {"MAC"=>$mac,"RSSI"=>$rssi,"SNR"=>$snr}; ...