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

Hi guys can anyone help this novice with what is probably a very simple error/misunderstanding about using a reference for a hash table. I'm converting some UNIX shell scripts to Perl but depending on where the script is run a different environment is required. I've tried using Tilly's script to ingest the local environment but it refused to work on our site. Instead I've created a subroutine with 3 huge hash tables containing these different environments and depending on a parameter supplied the appropriate hash table will be walked and the environment set up accordingly. The first problem that I encountered was how to use the parameter to pick out the correct hash table. I've got round this by setting up a reference to each hash table. This works and the correct table is being called but I'm getting an error that I think is due to the way I'm trying to use this reference but.......I'm not sure how to resolve this? Can someone point out the error of my ways or is there a more obvious way to access these tables? The code below is the actual code that I've been testing minus about 800 hash table elements - it's not working for any elements!
#!/usr/bin/perl -w # ####################################### # Perl Modules Used #-------------------------------------- use strict ; use Cwd ; # Current Working + Directory Module use Mail::Sender; # Email module use lib '/home/interface/scripts/Perl_Modules' ; # Where ACC_Vario +us lives # sub ACC_IHS_PROFILE { use strict ; # # # Scalar Variables #-----------------# my $db = undef ; my $DB_hash = undef ; my $key = undef ; my $paramno = undef ; my $value = undef ; # # Array Variables #---------------# # # Boolean Variables #-----------------# my $PARAMNO = 0 ; my $PARAMETER_ERROR = 0 ; # # HASH Table for LIVE environment #-------------------------------# my %LIVE = ( "ADMIN_DIR" => "/opt/bin1", ); # # HASH Table for DEVLP environment #--------------------------------# my %DEVLP = ( "ADMIN_DIR" => "/opt/bin2", ); # # HASH Table for TEST environment #--------------------------------# my %TEST = ( "ADMIN_DIR" => "/opt/bin3", ); # my %DBhashlookup = ( "TEST" => [\%TEST], "LIVE" => [\%LIVE], "DEVLP" => [\%DEVLP], ) ; # P R O C E S S I N G #-------------------# $paramno = (@_) ; # if ($paramno == 0) { print "\n\tNo parameters supplied - can't set up environment!\n" + ; $PARAMETER_ERROR = 1 ; } if ($paramno > 1) { print "\n\tToo many parameters supplied - Highlander!\n" ; $PARAMETER_ERROR = 1 ; } if (! $PARAMETER_ERROR) { $db = $_[0] ; if (($db ne "LIVE") && ($db ne "TEST") && ($db ne "DEVLP")) { print "\n\tDB $db is not known to this script\n" ; $PARAMETER_ERROR = 1 ; } } if (! $PARAMETER_ERROR) { $DB_hash = $DBhashlookup{$db} ; while (($key, $value) = each %$DB_hash ) { print "\n\tKEY :: $key\n" ; print "\n\tVALUE :: $value\n" ; $ENV{$key} = $value ; } } if ( $PARAMETER_ERROR ) { return 0 ; } else { return 1 ; } # } # # Scalar Variables #----------------# my $db = 'DEVLP' ; my $key = undef ; my $script = "xxrctest" ; my $value = undef ; # # Array Variables #----------------# # # Boolean Variables #-----------------# print "\n\t$script S T A R T S\n" ; &ACC_IHS_PROFILE($db) or die "\n\tBottom - Sub Failed!\n" ; print "\n\t$script E N D S\n" ;

The error message looks like an unwinding error to a numpty like me but here it is anyway.
xxrctest S T A R T S Argument "/opt/bin2" isn't numeric in each at ACC_IHS_PROFILE.pl line +85. Bad index while coercing array into hash at ACC_IHS_PROFILE.pl line 85 +.

Please let me know what folly I'm commiting here as I can't see it and it's begining to drive me insane(r)! Cheers, Ronnie

Replies are listed 'Best First'.
Re: Hash Table References?
by liverpole (Monsignor) on Jun 20, 2006 at 10:44 UTC
    Hi Ronnie,

    The first thing I noticed when trying to run your program is the errors:

    Pseudo-hashes are deprecated at ./x line 86. Pseudo-hashes are deprecated at ./x line 86. Argument "/opt/bin2" isn't numeric in each at ./x line 86. Bad index while coercing array into hash at ./x line 86.

    If you modify the program to display the contents of DB_hash just prior to where the error occurs:

    use Data::Dumper; printf "DEBUG: Dump of DB_hash = (%s)\n", Dumper($DB_hash); while (($key, $value) = each %$DB_hash ) {

    you'll see that it's actually an Array of Hashes:

    xxrctest S T A R T S DEBUG: Dump of DB_hash = ($VAR1 = [ { 'ADMIN_DIR' => '/opt/bin2' } ];

    So you need to dereference the first element of the array to get to the hash that it contains.  An easy way to do this would be:

    my $actual_hash = $DB_hash->[0]; while (($key, $value) = each %$actual_hash ) {

    In general you should try applying this debugging technique to any program which gives you warnings or errors, as it will usually help you get right to the source of the problem.


    s''(q.S:$/9=(T1';s;(..)(..);$..=substr+crypt($1,$2),2,3;eg;print$..$/
Re: Hash Table References?
by GrandFather (Saint) on Jun 20, 2006 at 10:57 UTC

    It's good to see you using use strict;. However you only need it once in your script (although may be this is a couple of scripts combined to show the issue, in which case good work and ignore the comment).

    I recommend that you return as soon as you find and deal with an error so you don't clutter the following code with redundant tests:

    if ($paramno == 0) { print "\n\tNo parameters supplied - can't set up environment!\n" + ; return 0; }

    However the real problem seems to be that you are creating an array containing a single element which is a reference to a hash in your lookup hash, where I suspect you really just want a reference thus:

    my %DBhashlookup = ( "TEST" => \%TEST, "LIVE" => \%LIVE, "DEVLP" => \%DEVLP, ) ;

    Making that change and running the code prints:

    xxrctest S T A R T S KEY :: ADMIN_DIR VALUE :: /opt/bin2 xxrctest E N D S

    DWIM is Perl's answer to Gödel
      Thanks that did the trick! I did wonder about the list context in the DBHASHLOOKUP table but not hard enough!! Once again many thanks.
      Ronnie
Re: Hash Table References?
by johngg (Canon) on Jun 20, 2006 at 10:58 UTC
    You can construct a reference to a hash of hashes all in one go. The variable $rhEnvInfo below will hold a reference to the hash keyed by environment name and whose values are references to further hash tables containing information for each environment. Values for a particular environment can be accessed by de-refencing with the -> operator. Like this

    use strict; use warnings; my $rhEnvInfo = { LIVE => {ADMIN_DIR => q{/opt/bin1}}, DEVLP => {ADMIN_DIR => q{/opt/bin2}}, TEST => {ADMIN_DIR => q{/opt/bin3}} }; my $try = q{DEVLP}; print qq{ADMIN_DIR for $try is }, qq{$rhEnvInfo->{$try}->{ADMIN_DIR}\n};

    When run this prints

    ADMIN_DIR for DEVLP is /opt/bin2

    I hope this helps.

    Cheers,

    JohnGG