http://www.perlmonks.org?node_id=1039009

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

Oh wise and all-knowing gurus...

I am writing a module using Moose that will serve as a wrapper to Net::SSH2 to log into an appliance, grab system information, and return it back to the caller for system monitoring purposes.

My issue is around references. In my module, references aren't working as I would expect them to. I have created a simplified version of the module that replicates the issue to include here.

Here is the code, below. However, please look at lines 125-126 and 131-134 (in the execute() method). If I use the code as pasted here, everything works both hunky and dory. It returns the output of the command run on the appliance as expected. However, if I uncomment lines 125,131-132 and comment out lines 126,133-134, it fails, usually telling me that line 133 is not a GLOB reference. What am I doing wrong?

Thanks.

package Foo; use v5.14; use strict; use warnings; use Carp; use Moose; use MooseX::StrictConstructor; use Net::SSH2; has 'hostname' => ( isa => 'Str', is => 'rw', ); has 'username' => ( isa => 'Str', is => 'rw', ); has 'password' => ( isa => 'Str', is => 'rw', ); has 'command' => ( isa => 'Str', is => 'rw', ); has '_ssh2_connection' => ( is => 'bare', isa => 'Object', ); has '_ssh2_channel' => ( is => 'bare', isa => 'Object', ); ## # Connect to server using provided parameters. # sub connect { my $self = shift; ## # Net::SSH2 connection object. # my $connection = Net::SSH2->new(); # Connect to server. $connection->connect($self->hostname()) or croak "Unable to connect to " . $self->hostname() . ": +$@\n"; $connection->auth_password($self->username(),$self->password()) or croak "Unable to authenticate to " . $self->hostname() . " as " . $self->username . ": $@\n"; ## # Net::SSH2::Channel object. # my $channel = $connection->channel(); # Open Net::SSH2::Channel shell so that multiple commands # can be executed. $channel->blocking(0); $channel->shell(); # Manually flush channel. Uncertain why this must be done, but # when used in an object wrapper, execute() will fail later if # it is not. using $channel->flush() doesn't do the trick. print $channel "\n"; while (<$channel>) { chomp; push my @tmp,"$_\n"; } # Attach connection and channel to object. $self->{_ssh2_channel} = \$channel; $self->{_ssh2_connection} = \$connection; return 1; } ## # Disconnect from server. # sub disconnect { my $self = shift; # Close Net::SSH2 objects. ${$self->{_ssh2_channel}}->close() if (defined ${$self->{_ssh2_cha +nnel}}); ${$self->{_ssh2_connection}}->disconnect() if (defined ${$self->{_ +ssh2_connection}}); # Delete Net::SSH2 objects. $self->{_ssh2_channel} = undef; $self->{_ssh2_connection} = undef; return 1; } ## # Execute command specified by 'command' attribute. # sub execute { # Initialize variables. my $self = shift; #my $channel = ${$self->{_ssh2_channel}}; my $channel = $self->{_ssh2_channel}; my @return_data; my $cmd = $self->command(); # Execute command and process return data. #print $channel "$cmd\n"; #while (<$channel>) { print {${$channel}} "$cmd\n"; while (<${$channel}>) { chomp; push @return_data,"$_\n"; } # Return array ref to caller. return \@return_data; } 1; # End of module. __PACKAGE__->meta->make_immutable; __END__

...and here is a test file, if you'd like to use it...

use v5.14; use strict; use warnings; use Foo; # Define object parameters. use constant { HOSTNAME => 'host.example.com', USERNAME => 'username', PASSWORD => 'password', COMMAND => 'ls -la', }; # Create the object. my $rt = Foo->new({ hostname => HOSTNAME, username => USERNAME, password => PASSWORD, command => COMMAND, }); # Connect to server and execute. $rt->connect(); my $rv = $rt->execute(); $rt->disconnect(); # Print the results. print "$_" foreach (@{$rv}); print "*** DONE ***\n";

Replies are listed 'Best First'.
Re: Question regarding using references with Net::SSH2 in module
by Preceptor (Deacon) on Jun 14, 2013 at 20:58 UTC

    Assuming I'm looking at the right lines, you're contemplating the difference between:

    #print $channel "$cmd\n"; #while (<$channel>) { print {${$channel}} "$cmd\n"; while (<${$channel}>) {

    That's because you're having a problem with passing references to filehandles. $channel is a filehandle. You're passing it by reference:

    $self->{_ssh2_channel} = \$channel;

    Which means when you're trying to use it - as a filehandle - you need to dereference first. Consider if you will:

    #!/usr/bin/perl use strict; use warnings; sub print_to_fh { my ( $ref_to_fh ) = @_; #$ref_to_fh is _not_ a filehandle. It's a scalar, that's a reference +. print $ref_to_fh "Some text\n"; } open ( my $filehandle, ">", "testfile.txt" ); &print_to_fh ( \$filehandle ); close ( $filehandle );

    This will give you the same error - because what you're passing _into_ the subroutine is not a filehandle, it's a reference to a filehandle.

    sub print_to_fh { my ( $ref_to_fh ) = @_; #$ref_to_fh is _not_ a filehandle. It's a scalar, that's a reference +. my $filehandle = $$ref_to_fh; #Filehandle has dereferenced $ref_to_fh, so we can print to it now: print $filehandle "Some more text\n"; } open ( my $filehandle, ">", "testfile.txt" ); &print_to_fh ( \$filehandle ); close ( $filehandle );

    This works, because the filehandle has dereferenced. I think this is what is happening in your code - a filehandle is basically a reference to a file, and you are passing a reference _to_ that reference.

    Edit: Check 'perldoc -f print': If you're storing handles in an array or hash, or in general whenever you're using any expression more complex than a bareword handle or a plain, unsubscripted scalar variable to retrieve it, you will have to use a block returning the filehandle value instead...

    Therefore in the example above, you could instead do:

    print {$$ref_to_fh} "Even more stuff\n";

    That's the essence of what that 'not a GLOB' message means - print doesn't like (recognise) your filehandle