unable to capture stderr while performing openssh to a variable- perl - perl

I want to capture the standard error displayed on host machine after (ssh->capture) to a variable.
for example when i try:
use Net::OpenSSH;
my $ssh = Net::OpenSSH->new($host);
my $out=$ssh->capture("cd /home/geek");
$ssh->error and
die "remote cd command failed: " . $ssh->error;
out put is:
child exited with code 1 at ./change_dir.pl line 32
i am not able to see what is the error. i get no such file or directory on the terminal. I want to capture the same "no such file or director" in $out.
example 2,
my ($stdout,$stderr)=$ssh->capture("cd /home/geek");
if($stderr)
print"Error = $stderr";
else
print "$stdout"
i see "Error=" printed but does not seee that $stderr on the screen.
i see $stdout is printed on success but print $stderr does not get printed only"Error= " gets printed.

When an error occurs it is most likely not going to be in STDOUT, and if it is in STDERR you are not catching that. You need to get to the application's exit code, in the following way. (Given the update to the question which I only see now: See the end for how to get STDERR.)
After the capture method you want to examine $? for errors (see Net-OpenSSH). Unpack that to get to the exit code returned by what was actually run by $ssh, and then look in that application's docs to see what that code means
$exit_code = $?;
if ($exit_code) {
$app_exit = $exit_code >> 8;
warn "Error, bit-shift \$? --> $app_exit";
}
The code to investigate is $app_exit.
An example. I use zip in a project and occasionally catch the error of 3072 (that is the $?). When that's unpacked as above I get 12, which is zip's actual exit. I look up its docs and it nicely lists its exit codes and 12 means Nothing to update. That's the design decision for zip, to exit with 12 if it had no files to update in the archive. Then that exit gets packaged into a two-byte number (in the upper byte), and that is returned and so it is what I get in $?.
Failure modes in general, from system in Perl docs
if ($? == -1) { warn "Failed to execute -- " }
elsif ($? & 127) {
$msg = sprintf("\tChild died with signal %d, %s coredump -- ",
($? & 127), ($? & 128) ? 'with' : 'without');
warn $msg;
} else {
$msg = sprintf("\tChild exited with value %d -- ", $? >> 8);
warn $msg;
}
The actual exit code $? >> 8 is supplied by whatever ran and so its interpretation is up to that application. You need to look through its docs and hopefully its exit codes are documented.
Note that $ssh->error seems designed for this task. From the module's docs
my $output = $ssh->capture({ timeout => 10 }, "echo hello; sleep 20; echo bye");
$ssh->error and warn "operation didn't complete successfully: ". $ssh->error;
The printed error needs further investigation. Docs don't say what it is, but I'd expect the unpacked code discussed above (the question update indicates this). Here $ssh only runs a command and it doesn't know what went wrong. It merely gets back the command's exit code, to be looked at.
Or, you can modify the command to get the STDERR on the STDOUT, see below.
The capture method is an equivalent of Perl's backticks (qx). There is a lot on SO on how to get STDERR from backticks, and Perl's very own FAQ has that nicely written up in perlfaq8. A complication here is that this isn't qx but a module's method and, more importantly, it runs on another machine. However, the "output redirection" method should still work without modifications. The command (run by $ssh) can be written so that its STDERR is redirected to its STDOUT.
$cmd_all_output = 'your_whole_command 2>&1';
$ssh->capture($cmd_all_output);
Now you will get the error that you see at the terminal ("no such file or directory") printed on STDOUT and so it will wind up in your $stdout. Note that one must use sh shell syntax, as above. There is a big bit more to it so please look it up (but this should work as it stands). Most of the time it is the same message as in the exit code description.
The check that you have in your code is good, the first line of defense: One should always check $? when running external commands, and for this the command to run need not be touched.

Related

How can I identify if the executed ghostscript command returned an error in Perl?

I am having problem to get ghostscript's error return code to know if it failed or not,
I tried corrupting a PostScript file (messed it up on a file editor) and ran it, but I could not get a 1 or non-0 return.
After reading some manual and topics about it(hardly) here, this is what I currently do:
my $cmd = "/apps/gs/ghostpdl-9.52/bin/gs -dSAFER -dBATCH -dNOPAUSE -sDEVICE=ps2write -sstdout=%stderr -sOutputFile=$output -sPAPERSIZE=a4 -dFIXEDMEDIA -f $psfile";
logmsg("command: $cmd");
my $rc = `$cmd`;
logmsg("rc: $rc");
if($rc != 0){
...
}
but $rc doesn't have any value so I couldn't satisfy $rc != 0
Hopefully someone could help.
Thanks
If the command outputs an error message to STDERR (and many do), then you need to append 2>&1 to the command in order to redirect STDERR to STDOUT for capture by the backticks, like so:
my $output = `$cmd 2>&1`;
Then check $output for some kind of error or success message. I suggest printing it in order to see to see what it contains.
Note that the return value of backticks is the output of the command, not it's return status! If you really want the native return status of the command executed by the backticks, you can get that information using $? or ${^CHILD_ERROR_NATIVE}. See perldoc perlvar for details.

Why exit is not always called in perl?

I have a perl script for setting up a remote host. And this is its interrupt handler in case if something will go wrong:
sub interrupt
{
$SIG{'__DIE__'} = '';
my $signal = shift;
print STDERR "Error $SELF_NAME: Bootstrapping of host '$REMOTE_HOST' is interrupted with error '$signal', deleting remote temporary directory $REMOTE_TEMP_DIR.\n";
remote_exec("perl -e \"use File::Path; rmtree('$REMOTE_TEMP_DIR');\"", $REMOTE_HOST, $REMOTE_EXEC_METHOD, $REMOTE_EXEC_PORT, $USERNAME, $PASSWORD, 0, 1);
exit 1;
}
And this handler is always called when there is a need. So I can see the error about interrupted bootstrapping in STDERR. But exit 1; is not called and script returns with exit_code = 0. BUT if I add this line print STDERR "After remote_exec and before exit"; between last two lines of my handler it works fine (i.e. returns with exit_code = 1).
Note: remote_exec just calls system($COMMAND) inside as I'm testing it on a local host.
UPDATE
Adding some details about how the script is being called:
I run the script from my C++ program which tracks its standard logs and checks exit status and in case when exit status is not equal to 0 it prints some error. So, when I add some extra line in my script between exit and system I can see the error my C++ program prints, but if there is not such extra line the C++ program tells that the script is successfully exited, which means that exit status is 0.
You didn't actually demonstrate the problem, so I had to guess at how to demonstrate it, and failed.
$ perl -e'
sub interrupt {
$SIG{"__DIE__"} = "";
my $signal = shift;
print STDERR "...\n";
system("echo ...");
exit 4;
}
$SIG{INT} = \&interrupt;
<>;
'
^C...
...
$ echo $?
4
(Used 4 cause it's more distinctive than 1.)
What do you even mean by "not called"? You seem to indicate the program did exit as a result of the interrupt, which means it got called.

How to run an executable file using Perl on Windows XP?

How to run an executable file using perl?
For instance, i want to run a plain notepad.exe. How could I achieve this?
This is what I've got:
my #args = system("notepad.exe");
system(#args) == 0 or die "system #args failed: $?";
But it returns:
Can't spawn "cmd.exe": No such file or directory blah blah blah.
What am I missing?
Your code seems a bit confused. What you probably want is something like
my $cmd = "notepad.exe";
my #args = ($cmd, "readme.txt");
system(#args);
if($? == -1) {
die "system #args failed: $?";
}
system returns a single value, not an array. See perldoc -f system for a detailed description.
This thread on perlmonks discusses the error you're getting with a few different solutions being presented.
This answer is an extension of my original comment. Sorry if it's superfluous.
Try this.
my $prog = "C:\\strawberry\\perltest\\Extractor.bat";
if (-f $prog) # does it exist?
{
print "Will run notepad";
system($prog);
}
else
{
print "$prog doesn't exist.";
}
This is a Perl internal error probably caused by a broken environment. Perl can't find the Windows shell cmd.exe that is used under the hood to run the program passed to system.
Use some utility as Process Monitor to see what's going on at the OS level.

Unable to close print file handle in perl

I have the following perl snippet:
open FH, " | lpr ";
print FH "Hello";
print FH "This is a print test";
close FH or die "can't close: $! $?";
I am getting the following output:
can't close: 256 at <filename> line 4
Any help would be appreciated..
Thanks in advance :)
From perldoc -f close
If the file handle came from a piped open, "close" will
additionally return false if one of the other system
calls
involved fails, or if the program exits with non-zero
status.
The missing error is probably due to your lpr-process is not done, or something went wrong there. Did the print work?
If the filehandle came from a piped open, close returns false if one of the other syscalls involved fails or if its program exits with non-zero status. If the only problem was that the program exited non-zero, $! will be set to 0 . Closing a pipe also waits for the process executing on the pipe to exit--in case you wish to look at the output of the pipe afterwards--and implicitly puts the exit status value of that command into $? and ${^CHILD_ERROR_NATIVE}.

How do I avoid printing to STDOUT when using Perl's `IPC::System::Simple:runx`?

I'm using IPC::System::Simple:runx to execute system commands and die on unexpected return values. The problem is that the commands output is printed to the shell.
How can I avoid printing this
output?
How can I avoid printing this output
but getting it into a perl variable?
UPDATE
3) How can I print this output iff the execution fails?
The capture() command? Or capturex().
Quoted from link:
Exception handling
In the case where the command returns an unexpected status, both run and capture will throw an exception, which if not caught will terminate your program with an error.
Capturing the exception is easy:
eval {
run("cat *.txt");
};
if ($#) {
print "Something went wrong - $#\n";
}
See the diagnostics section below for more details.
If a module does behave very nasty and prints directly to STDOUT you can always redirect STDOUT to something else. This sort of a hack but some modules require it.
# Save STDOUT for restore later
open(OLD_STDOUT, ">>&STDOUT");
open(STDOUT, ">/some/file/or/dev/null");
# call your module
# Restore STDOUT
open(STDOUT, ">>&OLD_STDOUT");