Home > Software design >  table of hash: efficiently count hashes matching a key
table of hash: efficiently count hashes matching a key

Time:07-07

I want to check the json output of ip -j adds show eno1 in perl. I want to count how many ipv4 addr the Nic has. I still need the ipv6 info so I want to avoir running twice /usr/bin/ip command with the -4 and then -6 flag.

For now I can access to the ip information like this: $nic->{addr_info}[0]->{local} For each entry, the $nic->{addr_info}[$ip_info_index]->{family} will give the IP type (inet or inet6)

In most cases 2 entries in my table: one for v4 and one for v6, but sometimes I have 2 v4 entries a and want to issue a warning "not supported" in my software.

$nic->{addr_info}[$ip_info_index]->{family} will give the type of entry.

Is there some elegant trick using map and scalar to count how many $nic->{addr_info}[$ip_info_index]->{family} are equal to 'inet' (and not 'inet6')

(I can loop over $ip_info_index , and increment a counter each time I see 'inet', but that seems not elegant).

  DB<3> p Dumper($nic)
$VAR1 = {
          'txqlen' => 1000,
          'address' => '00:26:b9:7d:c0:ee',
          'broadcast' => 'ff:ff:ff:ff:ff:ff',
          'link_type' => 'ether',
          'group' => 'default',
          'mtu' => 1500,
          'qdisc' => 'mq',
          'flags' => [
                       'BROADCAST',
                       'MULTICAST',
                       'UP',
                       'LOWER_UP'
                     ],
          'operstate' => 'UP',
          'ifindex' => 2,
          'addr_info' => [
                           {
                             'valid_life_time' => 30949,
                             'preferred_life_time' => 30949,
                             'label' => 'eno1',
                             'family' => 'inet',
                             'scope' => 'global',
                             'noprefixroute' => bless( do{\(my $o = 1)}, 'JSON::PP::Boolean' ),
                             'prefixlen' => 24,
                             'local' => '172.16.59.72',
                             'broadcast' => '172.16.59.255',
                             'dynamic' => $VAR1->{'addr_info'}[0]{'noprefixroute'}
                           },
                           {
                             'family' => 'inet6',
                             'local' => 'fe80::226:b9ff:fe7d:c0ee',
                             'valid_life_time' => 4294967295,
                             'preferred_life_time' => 4294967295,
                             'prefixlen' => 64,
                             'scope' => 'link'
                           }
                         ],
          'ifname' => 'eno1'
        };

for addr_info table I wantt to count how many hashes have 'family' => 'inet' (and same for 'inet6'). I need to fail if more that one ipv4 or one ipv6 is set. if you want to test on a linux system, the $nic is obtained like this:

my $ip_addr_output = `LC_ALL=C /sbin/ip -j addr 2>/dev/null`;

CodePudding user response:

I think this is what you want.

$nic->{addr_info} is a reference to an array where each element describes one of the IP addresses attached to the interface.

So @{ $nic->{addr_info} } dereferences that array so you can now pass it to functions that require arrays or lists. One such function is grep which filters a list and only returns elements in the list which satisfy some criteria. We can therefore get a list of IPv4 addresses using:

grep { $_->{family} eq 'inet' } @{ $nic->{addr_info} }

If you call `grep in scalar context, it doesn't give you the list, it gives you the number of items in the list.

scalar grep { $_->{family} eq 'inet' } @{ $nic->{addr_info} }

So you can use something like this:

say "$nic->{ifname} : ", scalar grep { $_->{family} eq 'inet' } @{ $nic->{addr_info} };
  • Related