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

Respected Monks & Gurus - I am newbie, learning the ways of perl. I am trying to pass a hash to the subroutine, one of the keys of hash is an array of hashes. I am unable to access the array properly. Heres my code:
sub do_ssh2 { my ($host,$server) = @_; my $username = $$server{"username"}; my $passwd = $$server{"passwd"}; my @cmdarr = $$server{"cmds"}; # <-is this correct my ($stdout, $stderr, $exit); my $ssh = Net::SSH::Perl->new($host, debug => 1); eval {$ssh->login($username, $passwd)}; if ($@) {logit ("ERROR: Wrong password for $host"); $$server{"stat +us"}="Y";} else {logit ("INFO: Logged in $host");$$server{"status"}="N";} eval {$ssh->login($username, $passwd)}; for (my $i=0; $i < @cmdarr; $i++) { my %cmdhash=$cmdarr[$i]; my $cmdname= $cmdarr[$i]{cmdname}; # doesnt work right print "\n\n$cmdarr[$i]{cmdname}\n\n"; eval{($stdout, $stderr, $exit) = $ssh->cmd($cmdname)}; if ($@) { logit ("ERROR: Some error, running $cmdname on $host"); $$server{"cmds"}[$i]{status}="N"; } else { logit ("INFO: executed $cmdname on $host"); $$server{"cmds"}[$i]{status}="Y"; $$server{"cmds"}[$i]{stdout}="$stdout"; $$server{"cmds"}[$i]{stderr}="$stderr"; $$server{"cmds"}[$i]{exit}="$exit"; } } } my %hosts=csv2hash("server_02.csv"); my $s=$hosts{"f8.lab.x.httpd.in"}; do_ssh1("f8.lab.x.httpd.in",$s);
The error that I am see is:
Reference found where even-sized list expected at ./Perl-1.pl line 121 +. Pseudo-hashes are deprecated at ./Perl-1.pl line 123.
The %hosts hash is of the following form:
%hosts = ( "s192.lab.x" => { status => "Y", username => "pluto", passwd => "ada232mok", cmds =>[ {cmdname => "uname",}, {cmdname => "pwd",}, ] }, "f8.lab.x.httpd.in" => { status => "Y", username => "pluto", passwd => "grs992ksj", cmds =>[ {cmdname => "uname", status => "Y", stdout => "Linux", stderr => "", exit => "0",}, {cmdname => "pwd", status => "Y", stdout => "/home/apps/plutarch", stderr => "", exit => "0",}, ] }, "sap.lab23.comp.iitd.x" => { status => "Y", username => "pluto", passwd => "ree212pkt", cmds =>[ {cmdname => "uname", status => "Y", stdout => "Linux", stderr => "", exit => "0",}, {cmdname => "pwd", status => "Y", stdout => "/home/apps/plutarch", stderr => "", exit => "0",}, ] }, );

Replies are listed 'Best First'.
Re: Pseudo-hashes & Hashes
by shmem (Chancellor) on Mar 16, 2008 at 23:57 UTC
    my @cmdarr = $$server{"cmds"}; # <-is this correct

    No, because a value in a hash (anonymous or not) is a scalar - which can be an array reference, of course. So, judging from your data, the correct idiom would be

    my @cmdarr = @{$server->{"cmds"}};

    For me, it is much simpler to de-reference references with the -> notation, so I would write

    $server->{"cmds"}->[$i]->{status}

    instead of

    $$server{"cmds"}[$i]{status}

    What does your csv2hash function return? A list or a reference? Check that, it could be the cause of "Reference found where even-sized list expected ...".

    --shmem

    _($_=" "x(1<<5)."?\n".q·/)Oo.  G°\        /
                                  /\_¯/(q    /
    ----------------------------  \__(m.====·.(_("always off the crowd"))."·
    ");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}

      Only the first -> is necessary. I would write it as

      $server->{cmds}[$i]{status}

      But i concur with your point, in general its easier to understand the -> style, as it reduces the possible meanings of $$ in your code (it has multiple meanings which depend on what follows, sometimes quite far after). In fact some people even avoid using bare scalar dereferencing on simple scalars just to avoid having $$ in their code. So they write ${$scalar_ref} instead of $$scalar_ref. (Im not in this camp personally).

      ---
      $world=~s/war/peace/g

Re: Pseudo-hashes & Hashes
by ikegami (Patriarch) on Mar 16, 2008 at 22:50 UTC

    Dunno if this is line 121, but
    my %cmdhash=$cmdarr[$i];
    would cause that warning. It should be
    my %cmdhash=%{$cmdarr[$i]};
    Then again, you don't even use that variable!

    Update:
    By the way, I avoid
    for (my $i=0; $i < @cmdarr; $i++)
    in favour of
    for my $i ( 0.. $#cmdarr )
    It has far less visual clutter. But wouldn't the following be even better?
    for my $cmd ( @cmdarr )

Re: Pseudo-hashes & Hashes
by FunkyMonk (Bishop) on Mar 16, 2008 at 22:37 UTC
    Reference found where even-sized list expected at ./Perl-1.pl line 121. Pseudo-hashes are deprecated at ./Perl-1.pl line 123.
    We'd find it easier to help you if you told us which line is 121. It would also help if we saw the call to do_ssh2

    Perhaps reading How (Not) To Ask A Question and I know what I mean. Why don't you? would help you to help us.

Re: Pseudo-hashes & Hashes
by grizzley (Chaplain) on Mar 17, 2008 at 08:33 UTC

    While using nested hashes/arrays, you are retrieving references to arrays/hashes instead of arrays/hashes. So here are few corrections:

    sub do_ssh2 { my ($host,$server) = @_; my $username = $$server{"username"}; my $passwd = $$server{"passwd"}; # got reference to array here... my $cmdarr_ref = $$server{"cmds"}; # <-is this correct my ($stdout, $stderr, $exit); my $ssh; # ...so here use as array reference... for (my $i=0; $i < @$cmdarr_ref; $i++) { # ...here you get reference, too (hash reference) my $cmdhash_ref=$$cmdarr_ref[$i]; my $cmdname= $$cmdarr_ref[$i]{cmdname}; # doesnt work right #or: my $cmdname= $$cmdhash_ref{cmdname}; print "\n\n$$cmdarr_ref[$i]{cmdname}\n\n"; { # logit ("INFO: executed $cmdname on $host"); $$server{"cmds"}[$i]{status}="Y"; $$server{"cmds"}[$i]{stdout}="$stdout"; $$server{"cmds"}[$i]{stderr}="$stderr"; $$server{"cmds"}[$i]{exit}="$exit"; } } }

    Maybe reading Arrays and hashes only with references would help you understand structures a little more?