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

Hi Monks, I am writing a script to generate a Array of Hashes by reading a input file which contains the text below

I was not able to generate the data for the interface gif0, Can I please know if there is also a better way to parse this file ?

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP> inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 nd6 options=201<PERFORMNUD,DAD> gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280 en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 ether f4:0f:24:29:df:4d inet6 fe80::1cb5:1689:1826:cc7b%en0 prefixlen 64 secured scopeid 0 +x4 inet 10.176.85.19 netmask 0xffffff00 broadcast 10.176.85.255 nd6 options=201<PERFORMNUD,DAD> media: autoselect status: active en1: flags=963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX> mtu 1500 options=60<TSO4,TSO6> ether 06:00:58:62:a3:00 media: autoselect <full-duplex> status: inactive p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304 ether 06:0f:24:29:df:4d media: autoselect status: inactive

Below is my code

#!/usr/bin/perl use warnings; use strict; use Data::Dumper; ################ my @AoH; my $rec; my $flag = 0; open my $fh, '<', 'interface.txt' or die; while (<$fh>) { chomp; if (/^(\w+\d{1}):\s+flags=(.*?>)/) { $flag = 1; $rec = {}; $rec->{'interface'} = $1; $rec->{'flags'} = $2; } elsif (/ether\s+(.*$)/) { $rec->{'ether'} = $1; } elsif (/media:\s+(\w+)/) { $rec->{'media'} = $1; } elsif (/inet\s+(\w+\.\w+\.\w+\.\w+)/) { $rec->{'inet'} = $1; } elsif (/status: (\w+)/) { $rec->{'status'} = $1; } elsif ($flag) { push @AoH, $rec; $flag = 0; } } close($fh); print Dumper \@AoH;

Output

./create_arr_of_hashes_from_file.pl $VAR1 = [ { 'flags' => '8049<UP,LOOPBACK,RUNNING,MULTICAST>', 'inet' => '127.0.0.1', 'interface' => 'lo0' }, { 'ether' => 'f4:0f:24:29:df:4d ', 'flags' => '8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTIC +AST>', 'status' => 'active', 'media' => 'autoselect', 'interface' => 'en0', 'inet' => '10.176.85.19' }, { 'status' => 'inactive', 'media' => 'autoselect', 'interface' => 'en1', 'flags' => '963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX +>', 'ether' => '06:00:58:62:a3:00 ' }, { 'status' => 'inactive', 'media' => 'autoselect', 'interface' => 'p2p0', 'flags' => '8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST>', 'ether' => '06:0f:24:29:df:4d ' } ];

Replies are listed 'Best First'.
Re: Generation of Array of hashes by reading a file
by huck (Prior) on Jan 04, 2018 at 20:26 UTC

    Doing a two step via flag is not needed

    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; ################ my @AoH; my $rec; open my $fh, '<', 'interface.txt' or die; while (<$fh>) { chomp; if (/^(\w+\d{1}):\s+flags=(.*?>)/) { $rec = {}; push @AoH, $rec; $rec->{'interface'} = $1; $rec->{'flags'} = $2; } elsif (/ether\s+(.*$)/) { $rec->{'ether'} = $1; } elsif (/media:\s+(\w+)/) { $rec->{'media'} = $1; } elsif (/inet\s+(\w+\.\w+\.\w+\.\w+)/) { $rec->{'inet'} = $1; } elsif (/status: (\w+)/) { $rec->{'status'} = $1; } } close($fh); print Dumper \@AoH;

      Slightly shorter loop:
      # After "open" .. my %wanted = map {$_=>1} qw|media inet status ether|; while (<$fh>) { chomp; if (/^(\w+\d{1}):\s+flags=(.*?>)/) { $rec = {}; push @AoH, $rec; $rec->{'interface'} = $1; $rec->{'flags'} = $2; next; } if (/(\w+):?\s+(.*$)/ and $wanted{$1}) { $rec->{$1} = $2; } }

                      We're living in a golden age. All you need is gold. -- D.W. Robertson.

      Thanks Huck and other Monks. All the solutions work. One of the doubt I had with this, Every time when the line matches the key related to interface, we are making the hash empty and then pushing that on to Array. I was assuming the Array will only contain a list of empty hashes, But not the case when I run the script with your solution. Can you please explain or help me understand the push statement after assigning empty hash to rec

        $rec is not a hash, it's a hash reference. When you change the referenced hash, all references pointing to it see the new value.

        my $hash_ref = {}; push my @arr, $hash_ref; $hash_ref->{key} = 'value'; print $arr[0]{key}; # value

        ($q=q:Sq=~/;[c](.)(.)/;chr(-||-|5+lengthSq)`"S|oS2"`map{chr |+ord }map{substrSq`S_+|`|}3E|-|`7**2-3:)=~y+S|`+$1,++print+eval$q,q,a,
Re: Generation of Array of hashes by reading a file
by tybalt89 (Monsignor) on Jan 05, 2018 at 00:34 UTC

    PUSH? We don't need no stinkin' PUSH!

    #!/usr/bin/perl use strict; use warnings; $_ = do { local $/; <DATA> }; my @AoH = map +{ interface => /^(\w+):\s+(flags)=(\S+)/, /(inet|ether|media|status):? (\S+)/g }, /.+?(?=^\w|\z)/gms; use Data::Dump 'pp'; pp \@AoH; __DATA__ lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 16384 options=1203<RXCSUM,TXCSUM,TXSTATUS,SW_TIMESTAMP> inet 127.0.0.1 netmask 0xff000000 inet6 ::1 prefixlen 128 inet6 fe80::1%lo0 prefixlen 64 scopeid 0x1 nd6 options=201<PERFORMNUD,DAD> gif0: flags=8010<POINTOPOINT,MULTICAST> mtu 1280 en0: flags=8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST> mtu 1500 ether f4:0f:24:29:df:4d inet6 fe80::1cb5:1689:1826:cc7b%en0 prefixlen 64 secured scopeid 0 +x4 inet 10.176.85.19 netmask 0xffffff00 broadcast 10.176.85.255 nd6 options=201<PERFORMNUD,DAD> media: autoselect status: active en1: flags=963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX> mtu 1500 options=60<TSO4,TSO6> ether 06:00:58:62:a3:00 media: autoselect <full-duplex> status: inactive p2p0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 2304 ether 06:0f:24:29:df:4d media: autoselect status: inactive

    Outputs:

    [ { flags => "8049<UP,LOOPBACK,RUNNING,MULTICAST>", inet => "127.0.0.1", interface => "lo0", }, { flags => "8010<POINTOPOINT,MULTICAST>", interface => "gif0" }, { ether => "f4:0f:24:29:df:4d", flags => "8863<UP,BROADCAST,SMART,RUNNING,SIMPLEX,MULTICAST>", inet => "10.176.85.19", interface => "en0", media => "autoselect", status => "active", }, { ether => "06:00:58:62:a3:00", flags => "963<UP,BROADCAST,SMART,RUNNING,PROMISC,SIMPLEX>", interface => "en1", media => "autoselect", status => "inactive", }, { ether => "06:0f:24:29:df:4d", flags => "8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST>", interface => "p2p0", media => "autoselect", status => "inactive", }, ]

    ( I like Data::Dump better :)

Re: Generation of Array of hashes by reading a file
by jwkrahn (Abbot) on Jan 04, 2018 at 21:35 UTC

    Your program can be simplified to:

    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; ################ my @AoH; open my $fh, '<', 'interface.txt' or die "Cannot open 'interface.txt' +because: $!"; while ( <$fh> ) { if ( / ^ ( \w+ \d ) : \s+ flags= ( \S+ ) /x ) { push @AoH, { interface => $1, flags => $2 }; } elsif ( / ( ether | media | inet | status ) :? \s+ ( \S+ ) /x ) { $AoH[ -1 ]{ $1 } = $2; } } close $fh; print Dumper \@AoH;
Re: Generation of Array of hashes by reading a file
by poj (Abbot) on Jan 04, 2018 at 21:41 UTC
    Can I please know if there is also a better way to parse this file ?

    Not sure about better ..

    #!/usr/bin/perl use warnings; use strict; use Data::Dumper; my @AoH; my $rec; my $re = join '|' ,'(?<k>ether)\s+(?<v>.*$)' ,'(?<k>media):\s+(?<v>\w+)' ,'(?<k>inet)\s+(?<v>\w+\.\w+\.\w+\.\w+)' ,'(?<k>status): (?<v>\w+)' ,'(?<k>broadcast)\s+(?<v>\w+\.\w+\.\w+\.\w+)' ,'(?<k>netmask)\s+(?<v>\w+)' ,'(?<k>mtu)\s+(?<v>\d+)' ,'(?<k>prefixlen)\s+(?<v>\d+)'; open my $fh, '<', 'interface.txt' or die; while (<$fh>) { chomp; if (/^(\w+\d{1}):\s+flags=(.*?>)/){ $rec = {}; push @AoH, $rec; $rec->{'interface'} = $1; $rec->{'flags'} = $2; } while (/$re/g) { $rec->{$+{k}} = $+{v}; } } close $fh; print Dumper \@AoH;
    poj