Reputation: 46979
In the code below I am trying to clone a git repository to another machine using a Perl script.
If I print $output
, I am getting all the login messages i.e, the contents of /etc/motd/
, but not the actual command output.
How to resolve this issue? Am I doing something wrong in the execution of the ssh command?
sub myexec_remote
{
my($cmd, $hostname, $filename) = @_;
my $stdout = $stderr = $exit= "";
my ($output) = `ssh $hostname $cmd 2>&1`;
## this is the command i.e, executed
##command is ssh 111.22.11.32 "git clone --bare [email protected]:/nfs/git/ /nfs/new123/"
$exit = $?;
if (defined $output)
{
open(MYOUTFILE, ">$filename");
print MYOUTFILE "$output";
close(MYOUTFILE);
}
}
I am using the backticks because sometimes ssh is done without passwordless and I have seen that the NET:SSH module doesn't support it...
Upvotes: 1
Views: 291
Reputation: 10242
use Net::OpenSSH:
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new($host, user => $user, password => $password);
my $output = $ssh->capture($cmd);
Upvotes: 0
Reputation: 39773
It is because you use the backticks operator in a list context:
my ($output) = `ssh $hostname $cmd 2>&1`;
When called in list context, backticks will return a list of lines. You only capture the first line, and put it in $output
. The other lines are ignored. From perldoc perlop
:
In scalar context, it comes back as a single (potentially multi-line) string, or undef if the command failed. In list context, returns a list of lines (however you've defined lines with $/ or $INPUT_RECORD_SEPARATOR), or an empty list if the command failed.
1. my $output = `ssh $hostname $cmd 2>&1`;
Calling backticks in scalar context will cause $output to be a multiline string.
2. my @output = `ssh $hostname $cmd 2>&1`;
You can call backticks fine in array context, but you'll have to print the entire array to the MYOUTPUTFILE
later on
These are not part of the solution. The solution itself should be enough to fix your bug. However, they can decrease the risk of bugs, and increase your Perl knowledge.
Try using the three-argument version of open, instead of your two-argument with a global, and catch the error when the file could not be opened:
open(my $handle, '>', $filename) or die "Unable to open file, $!";
Many people decide to use IPC::System::Simple instead of backticks, because you have more information when something goes wrong and you can avoid the shell.
Another option is IPC::Open3, which even lets you capture STDOUT, STDERR and the exitcode easily. It is a bit harder to use though.
Upvotes: 2