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

I am working on some code that reads in a text file with varying formats contained in it. There are a number of sections and each section has its own format with a header followed by a number of data rows. So I created a structure like this: Section_Hash->Data_Array_of_Hashes->Hashes_Containing_Data No problem. Data::Dumper spits it back nicely. but when I try to parse it with the following code I get "Not a HASH reference" error.
# # Dump_vconfig # # Display the contents of the VConfig hashes. # # Arguments: # # Returns: noerror on success otherwise error # sub Dump_vconfig { $Data::Dumper::Indent = 3; foreach my $Section (keys(%VConfig)) { print("Section\[$Section\]\n"); foreach my $Array ($VConfig{$Section}) { foreach my $Hash (@$Array) { foreach my $Key (keys(%$Hash)) { # # This is the line that generates the Not a Hash refere +nce error # | | | | | | | | | | # V V V V V V V V V V print("Key:\[$Key\]\[$Hash->{$Key}\]\n"); } } } } return(noerror); }
I know this has got to be something stupid on my part but I can't get my brain wrapped around it today. The Full code is under the read more tags.
#!/usr/bin/perl -w use strict; use constant error => -1; use constant noerror => 0; use constant false => 0; use constant true => 1; use Data::Dumper; # # Globals # my %Config = ( name => "VConfig_Parser", vconfig_path => '/ntos/vconfig.out', ldvid => '', node_id => '', ); my %Fields = ( 'system' => ['SYSTEMNAME', 'BLKS_PER_CYLINDER'], 'clique' => ['CL_ID', 'ARRAY_COUNT'], 'node' => ['CL_ID', 'NODE_ID', 'KIND', 'SLAN', 'CLAN', 'PDN', + 'TPA_OK'], 'channel' => ['NODE_ID', 'BUS', 'SLOT', 'PORT', 'KIND', 'CUA', ' +SPEED', 'HGID', 'LCU', 'CHANNEL', 'ADDRESS'], 'lan' => ['NODE_ID', 'BUS', 'SLOT', 'PORT', 'KIND', 'HGID'], 'pdisk' => ['CL_ID', 'VDISKID', 'NBLKS', 'REDUNDANCY', 'GBID', + 'TARG', 'LUN', 'SWAP BLKS', 'PARTITION NAME', 'ARRAY'], 'vproc' => ['CL_ID', 'VPROCID', 'TYPE', 'MOVABLE', 'PMA_ID', ' +HGID', 'VDISKID', 'PID_START', 'PID_COUNT'], 'disk_name' => ['pdisk global name', 'node', 'local device', 'Curr +ent Location'], ); my %VConfig; # # Main # $0 =~ /.*\/(.*)\..*/; $0 =~ /(.*)\..*/ unless defined($1); if($#ARGV < 1 or $#ARGV > 2) { die("Usage: $1 <ldvid> <node_id> [vconfig_path]"); } else { $Config{'ldvid'} = $ARGV[0]; $Config{'node_id'} = $ARGV[1]; if (defined($ARGV[2])) { $Config{'vconfig_path'} = $ARGV[2]; } } Parse_vconfig($Config{'vconfig_path'}); print("\n"); Dump_vconfig(); exit(noerror); # # Parse_vconfig # # Read the vconfig file, splitting it's sections into hashes # # Arguments: <FileName> # # Returns: noerror on success otherwise error # sub Parse_vconfig { my $file = shift; my $Big_Array; my $fh; my $endtime; my %Tokens = ( 'system' => 'SYSTEM', 'clique' => 'CLIQUE', 'node' => 'NODE', 'channel' => 'CHANNEL', 'lan' => 'LAN', 'pdisk' => 'PDISK', 'vproc' => 'VPROC', 'disk_name' => '# Global Disk Name to Local Device Mapping', ); # # Return undef if the File Doesn't Exist or is 0 Length # if(!-s $file) { die("File:[$file] Not Found or 0 Length."); return(error); } # # Open the File - Or print Error Message if there is a problem # if(!open($fh, "<", $file)) { die("Unable to open file:[$file]:\n$^E:\n$!:\nError Number $?:$@ + "); return(error); } my $In_Section = false; my $pdisk_global_name; # # Loop through the File a Line at a time # while(<$fh>) { chomp(); my $Line = $_; my $token; my $New_Section = false; foreach (keys(%Tokens)) { $token = $_; if ($Line =~ /^$Tokens{${token}}$/) { if ($In_Section) { $VConfig{$In_Section} = $Big_Array; # print(Dumper($Big_Array)); # getc(); } $In_Section = $token; # print("Found Section:\[${token}\]\n"); $Big_Array = (); $New_Section = true; last; } } # # Skip Section Headings # if ($New_Section) { next; } my $Values; my $Record; $Values = {}; $Record = (); if ($In_Section and $In_Section ne 'disk_name' and ($Line !~ /^# +.*/)) { @$Record = split(); my $Record_Number = 0; foreach (@$Record) { # print("In_Section:\[$In_Section\] Record_Number:\[$Recor +d_Number\] \$Fields\{\$In_Section\}\[\$Record_Number\]:\[$Fields{$In_ +Section}[$Record_Number]\] \$_:\[$_\]\n"); $Values->{$Fields{$In_Section}[$Record_Number]} = $_; $Record_Number++; } } # # Different Record Format for disk_name # elsif ($In_Section eq 'disk_name') { if ($Line =~ /^# pdisk global name:.*/) { $pdisk_global_name = (split(/ /,$Line))[4]; next; } elsif ($Line =~ /^# node:/) { my $node; my $local_device; my $Current_Location_Device; ($node, $local_device, $Current_Location_Device) = (split( +/\s+/,$Line))[2,5,9]; $Values->{'pdisk global name'} = $pdisk_global_name; $Values->{'node'} = $node; $Values->{'local device'} = $local_device; $Values->{'Current Location'} = $Current_Location_Device; } else { next; } } push(@$Big_Array, \$Values); } close($fh); # Close the File When Done $VConfig{$In_Section} = $Big_Array; return(noerror); } # # Dump_vconfig # # Display the contents of the VConfig hashes. # # Arguments: # # Returns: noerror on success otherwise error # sub Dump_vconfig { $Data::Dumper::Indent = 3; foreach my $Section (keys(%VConfig)) { print("Section\[$Section\]\n"); foreach my $Array ($VConfig{$Section}) { foreach my $Hash (@$Array) { foreach my $Key (keys(%$Hash)) { print("Key:\[$Key\]\[$Hash->{$Key}\]\n"); } } } } return(noerror); } __END__

Replies are listed 'Best First'.
Re: Not a HASH reference
by almut (Canon) on Apr 23, 2007 at 20:14 UTC

    From just looking at the code (I didn't run it for the same reasons liverpole mentioned), I'd say that you probably want to

    push(@$Big_Array, $Values);

    instead of pushing a reference to a hashref (\$Values)...

      Thanks! That was it.
Re: Not a HASH reference
by liverpole (Monsignor) on Apr 23, 2007 at 19:58 UTC
    Hi NateTut,

    When you present a full program like this, you're not making easy for us to help you.  I took a shot at running your program, and immediately found that it requires a lot of setup first, including passed arguements (who knows what they're supposed to be?), one or more configuration files (are we supposed to guess what the contents are?) and I don't know what else, because it gets too frustrating too quickly.

    Instead of doing this, why don't you just supply the subroutine Dump_vconfig and a single call to that subroutine, with the appropriate parameter that's failing.  You said that "Data::Dumper spits it back nicely", but then you don't show us what Data::Dumper gives you.  If you supply us with that, it'll be a LOT easier and faster to help you out.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
      As requested by liverpole the abbreviated Dumper output is below. The arguments "required" by the code are meaningless at this point and are a future requirement. The code does require a rather lengthy data file which I'd rather not post here if possible.

        Well, my hopes went up for a moment, when I saw that you had included data.

        Unfortunately, it doesn't help.

        All you've done is thrown 16, essentially nameless, data structures at me, with no idea how to duplicate your error.

        Do you not understand what I'm asking you for?  I'm perfectly happy to run some piece of code that you say is "broken", and help you figure out why it's not giving you what you want.  But you can't expect me to have your disk files on my disk, so whatever data is necessary has to be built into the program.

        I'm not going to peruse 16 different variables to try to figure out how you might have been using them originally in your program.  Were they variables passed to Dump_vconfig()?  Were they a dump of some configuration/data file?  It doesn't really matter, because I've spent enough time on this now without getting anywhere, and it's almost not worth the frustration any more.

        A final suggestion:  Give us a short, complete program which exhibits the problem (including the subroutine Dump_vconfig), and doesn't rely on the state of anyone else's machine.

        You're going to have to meet us halfway.

        If you can't do that, than all I can say is "good luck".


        s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/