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

I am writing a script to read many files with structured data, the ultimate goal is to create a CSV file where each line has the server name and the value of the fields.

I cant seem to build and array of hashes to hold the data, the application only inserts the first record

typical file data:
define host { host_name whplnsweb-dr use xiwizard_windowsserver_host alias whplnsweb-dr display_name whplnsweb-dr address 172.28.17.115 hostgroups AntiVirus End Point Protection +,windows-servers contacts helpdesk,admin register 1 }
my program:
use strict; use Data::Dumper; my @host = (); my $record = {}; my $key; my $value; my $line; my $rec_num = 0; my $filename = ""; my $dirname = "S:/Projects/Nagios/usr-local-nagios/etc/hosts"; opendir(DIR, $dirname) or die "cant opendir $dirname: $!"; while (defined ($filename = readdir(DIR))){ next if $filename =~ /host_data/; next if $filename =~ /^[\.]+/; print "filename: $filename \n"; open(FH, "<", $filename); while ($line = <FH>){ chomp $line; next if $line =~ /^$/; next if $line =~ /[#{}]/; $line =~ /^\s*(\S+)\s*(\S+)/; $record->{$1} = $2; } close(FH); push @host, $record; $rec_num =+ 1; } closedir(DIR); print Dumper(@host);

Replies are listed 'Best First'.
Re: array of hashes use
by bioinformatics (Friar) on Apr 02, 2013 at 01:05 UTC
    For the inner loop you can use an anonymous hash like:
    while ($line = <FH>) { chomp $line; next if $line =~ /^$/; next if $line =~ /[#{}]/; $line =~ /^\s*(\S+)\s*(\S+)/; # new portion $host[$counter]{$1} = {$2}; }
    You could push or simply use a counter (which keeps the code cleaner). Just one idea anyway :).

    Bioinformatics
Re: array of hashes use
by Anonymous Monk on Apr 01, 2013 at 21:32 UTC

    You only have one %$record, as declared at the top.

    I'm guessing that you probably want to have a new record for each file in the outer while loop, so put the my $record = {} inside that loop.

      Your are right! This code works as expected:

      use strict; use warnings; use Data::Dumper; my (@host, $key, $value, $line, $filename); my $rec_num = 0; my $dirname = "."; opendir( my $dir, $dirname) or die "cant opendir $dirname: $!"; while (defined ($filename = readdir($dir))){ next unless $filename =~ /\w+\.txt/; print "filename: $filename \n"; my $record = {}; open( my $fh , "<", $filename); while ($line = <$fh>){ chomp $line; next if $line =~ /^$/; next if $line =~ /[#{}]/; $line =~ /^\s*(\S+)\s*(\S+)/; $record->{$1} = $2; } close($fh); push @host, $record; $rec_num += 1; } closedir($dir); print Dumper(@host);

      This is the output:

      filename: hosts.txt filename: hosts1.txt $VAR1 = { 'contacts' => 'helpdesk,admin', 'display_name' => 'whplnsweb-dr', 'register' => '1', 'use' => 'xiwizard_windowsserver_host', 'hostgroups' => 'AntiVirus', 'host_name' => 'whplnsweb-dr', 'address' => '172.28.17.115', 'alias' => 'whplnsweb-dr' }; $VAR2 = { 'contacts' => 'helpdesk,admin', 'display_name' => '-------------', 'register' => '1', 'use' => 'test1.txt', 'hostgroups' => 'AntiVirus', 'host_name' => 'test1.txt', 'address' => '-------------', 'alias' => 'test1.txt' };

        Be that as it may, you either use autodie or explicitly check for the return of the open function in case it fails. The same goes for close and closedir.

        If you tell me, I'll forget.
        If you show me, I'll remember.
        if you involve me, I'll understand.
        --- Author unknown to me
Re: array of hashes use
by Anonymous Monk on Apr 02, 2013 at 14:43 UTC
    Thanks everyone, I didnt understand where to declare the $record variable. Once I moved int inside the loop it works properly. Sal