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

Hello Monks,

Long time viewer, first time poster. Love the work of everyone in this community.

I am attempting to SSH into a node, run and capture various different commands, then process the outputs and present to the user.

Using shell() the below script works, however I want to capture the command output so it can be process it before printing.

Is there a way to capture the output instead of printing it to the user?

#!/usr/bin/perl use Net::SSH2; my $ssh2 = Net::SSH2->new(); $ssh2 -> connect ($ip) or die $!; $ssh2 -> auth_password ($user,$pass); my $chan = $ssh2->channel(); $chan->shell(); print $chan "$cmd1\n"; print "$_" while <$chan>; print $chan "$cmd2\n"; print "$_" while <$chan>; print $chan "$cmd3\n"; print "$_" while <$chan>; print $chan "$cmd4\n"; print "$_" while <$chan>; $chan->close; $ssh2->disconnect;

Your assistance is greatly appreciated.

Replies are listed 'Best First'.
Re: Net::SSH2 to capture multiple commands on remote system
by salva (Canon) on Sep 16, 2014 at 08:44 UTC
    Well, nothing stops you from pushing the retrieved data in an array instead of passing it to print. For instance:
    $chan->shell(); print $chan "$cmd1\n"; @out1 = <$chan>; print $chan "$cmd2\n"; @out2 = <$chan>; ...

    Unfortunately, Net::SSH2 is full of quirks and using it in the way you are doing is unreliable. You can see it if you run some slow commands, for instance, try $cmd1 = sleep 2.

    If you are on a Linux/Unix system, your better option would be to switch to Net::OpenSSH.

    Otherwise, you can use Net::SSH::Any which is able to run over Net::SSH2 and provides a friendlier interface while taking care of the ugly details for you... well, mostly, because there are several bugs on the current stable version of libssh2 that can not be worked around.

      Salva, That was exactly what I was looking for. Works perfectly. Thank you so much! I have gone down the OpenSSH path and ran into a problem I believe is an incompatibility with the version on OpenSSH on the Unix server and the type of node I am communicating with. I also looked into your SSH::Any but was not getting any returns from my commands.
        That was exactly what I was looking for. Works perfectly

        No, it doesn't. That will fail to work correctly quite often.

Re: Net::SSH2 to capture multiple commands on remote system
by jellisii2 (Hermit) on Sep 16, 2014 at 11:35 UTC
    Control::CLI takes the messiness out of channel switching. It uses Net::SSH2 under the hood. I've found it to be extremely usable.
Re: Net::SSH2 to capture multiple commands on remote system
by thanos1983 (Parson) on Sep 16, 2014 at 11:52 UTC

    Hello bellis,

    Recently I had similar request like yours and I end up creating several different solutions. One of them is provided bellow. I am using Net::OpenSSH as the module for my purposes.

    On the example given bellow you are also able to execute sudo commands, something that I found difficult at the beginning but mandatory for my task. I have also created more complicated solutions with the same module reading from several servers sequentially, if you have similar problem just let me know and I will post working samples of code for you to modify based on your criteria.

    #!/usr/bin/perl use Expect; use strict; use warnings; use Data::Dumper; use Net::OpenSSH; select STDOUT; $| = 1; select STDERR; $| = 1; my $password = "Your Password Here"; my $port = 22; # default 22 if you have changed modify it my $timeout = 20; my $debug = 0; my $ssh = Net::OpenSSH->new( 'username@127.0.0.1' , password => $passw +ord , port => $port ); my ($pty, $pid) = $ssh->open2pty("sudo cat /etc/shells") # After a successful sudo operation, it doesn't request the password # again until some time after, handling this undeterministic behavior # is a pain in the ass, so we just clear any cached credentials # calling "sudo -k" first as follows: =comment my ($pty, $pid) = $ssh->open2pty("sudo -k; sudo ntpdate ntp.ubuntu +.com") or die "open2pty failed: " . $ssh->error . "\n"; =cut my $expect = Expect->init($pty); $expect->raw_pty(1); $debug and $expect->log_user(1); $debug and print "waiting for password prompt\n"; $expect->expect($timeout, ':') or die "expect failed\n"; $debug and print "prompt seen\n"; $expect->send("$password\n"); $debug and print "password sent\n"; $expect->expect($timeout, "\n") or die "bad password\n"; $debug and print "password ok\n"; my @array = (); my @final = (); while(<$pty>) { push (@array,$_); print "This is the return: $. $_" } foreach my $line (@array) { chomp($line); $_ = "Modify $line"; #$line =~ s/;//; push(@final,$_); } print Dumper(\@final); __END__ This is the return: 1 # /etc/shells: valid login shells This is the return: 2 /bin/sh This is the return: 3 /bin/dash This is the return: 4 /bin/bash This is the return: 5 /bin/rbash $VAR1 = [ ', 'Modify # /etc/shells: valid login shells ', 'Modify /bin/sh ', 'Modify /bin/dash ', 'Modify /bin/bash ' 'Modify /bin/rbash ];

    Other modules to take in consideration are Net::SSH::Perl and Net::Telnet. From my point of view they are a bit more complicated but in case you need some sample of code just let me know I have some samples that you can modify.

    Hope I did not confused you more than I simplified the solution to your problem.

    Seeking for Perl wisdom...on the process of learning...not there...yet!