Unable to read IPC::Open3 child's output - perl

This code is used to read and write through the pipe, but it seems it is not working well.
use strict;
use IPC::Open2;
my $st1="String1\n";
my $st2="String2\n";
my $st3="String3\n";
my $st4="String4\n";
my $st5="String5\n";
my $joint=$st1.$st2.$st3.$st4.$st5;
my $r;
my $pid = open2(\*CHILD_IN, \*CHILD_OUT, 'java -Dfile.encoding=UTF8 -cp abc.jar:xxx.jar TestCode')
or die "open2() failed $!";
print CHILD_IN $joint;
$r=<CHILD_OUT>;
print "Got $r from child\n";
print "[OUTPUT]: $_" while (<CHILD_OUT>);
This code is only reading first line of the output that too which is stored in $r. Not going inside while loop. Although there is a lot of output by executing the command.

You have a typo.
$r=<CHLD_OUT>;
and
print "[OUTPUT]: $_" while (<CHILD_OUT>);
It's CHLD_OUT versus CHILD_OUT.
It's probably better to always use
use warnings
It'd save you from the trouble, by showing a warning message:
readline() on unopened filehandle CHILD_OUT at x.pl line 19.

Related

Combine redirecting open() to scalar with system()

I wrote a routine to "safely" execute some command, and I wanted to capture STDOUT and STDERR in string variables using open(STDOUT, '+<', \$stdout) and similar for STDERR.
I verified via print "Test\n" and print STDERR "Test2\n" that the redirection works inside the routine (I can find the outputs in $stdout and $stderr).
However when I run the command through system() (Perl's version), the output still goes to the terminal.
So I wonder: Is opening a scalar value available for Perl's own I/O only?
And if so, how would I capture the STDOUT and STDERR from the system() call without using temporary files (having their own issues)?
(I've seen https://stackoverflow.com/a/109672/6607497 already)
The preferred solution (if such exists) should use as few extra packages as possible, and it should run with SLES 12 or SLES 15 (openSUSE Leap 15.2).
Those distributions only offer a limited set of Perl modules.
You can easily do this using IPC::Run to capture output.
Test script that writes to standard output and error:
#!/bin/sh
# demo.sh
echo "To Standard Output"
echo "To Standard Error" >&2
and perl script that runs it:
#!/usr/bin/env perl
use warnings;
use strict;
use IPC::Run qw/run/;
my ($out, $err);
run ["sh", "demo.sh"], \undef, \$out, \$err;
print "Standard output: ", $out;
print "Standard error: ", $err;
gives the following output:
$ perl demo.pl
Standard output: To Standard Output
Standard error: To Standard Error
Alternative using IPC::Run3 (Which might be more desirable if you don't need any of IPC::Run's more advanced features):
#!/usr/bin/env perl
use warnings;
use strict;
use IPC::Run3;
my ($out, $err);
run3 ["sh", "demo.sh"], \undef, \$out, \$err;
print "Standard output: ", $out;
print "Standard error: ", $err;

From inside a perl script can you know the name of the file you are redirecting output to?

So I have:
test.pl > test.log
is there a way to know inside test.pl that I am outputing to 'test.log'? At the end of my script I want to do some manipulation of test.log without hardcoding the name.
Maybe. The following works on Linux, but will not be very portable to other systems...
#!/usr/bin/env perl
use strict;
use warnings;
my $out = readlink("/proc/$$/fd/1");
print STDERR "I am being output to $out\n";
Naturally, this is probably a bad idea. Better to explicitly open the file and write to it in Perl, rather than having the shell set up redirections.
You can redirect standard output from perl, with minimal changes to your script,
test.pl test.log
my ($file) = #ARGV;
if (#ARGV) {
open STDOUT, ">", $file or die $!;
}
print "output is redirected to $file\n";
# use $file at the end

unable to generate error and redirect it to a file in perl

I am trying to redirect my STDOUT and STDERR to some file. I am successful with that to some extent. But i am not able to understand one thing in the below code.
#!/usr/bin/perl
open (STDOUT,">/var/tmp/outfile") or die "problem : $!";
open (STDERR,">>/var/tmp/outfile") or die "problem : $!";
print "$_\n" foreach (1..10);
sdsdf; # buggy line inserted wantedly
I have inserted the last line assuming that perl would throwout an error and that would be redirected to the file but its not happening . My program does not throughout any error onto the screen nor to the outfile. Please help me understand this behavior.
The sdsdf is not generating any errors (if you use strict then you'll see some compile time errors), that's why you are not seeing any messages. Try this:
use warnings;
use strict;
open (STDOUT,">outfile1") or die "problem : $!";
open STDERR, ">&STDOUT";
print "$_\n" foreach (1..10);
die("aaaa"); # buggy line inserted wantedly
Also in your code you are opening the same file twice, this might cause some problems. In the above we first redirect the stdout to a file then redirect stderr to stdout.
Without use strict;,
sdsdf;
is the same as
"sdsdf";
That's one of the reasons you always want to use use strict; use warnings;. Let's start by adding that.
So you want to log all output including compile-time errors to a file. Well, that's not going to happen by redirecting STDERR after your code has been compiled. The best way to do this is from outside your program.
script.pl >/var/tmp/outfile 2>&1
but it can be done from within your program.
#!/usr/bin/perl
use strict;
use warnings;
BEGIN {
open(STDOUT, '>', '/var/tmp/outfile')
or die("Can't redirect STDOUT: $!\n");
open(STDERR, '>&', \*STDOUT)
or die("Can't redirect STDERR: $!\n");
}
print "$_\n" foreach (1..10);
sdsdf; # Syntax error

command output is directed to the screen

novice Perl programmer here!.
I'm using the system() function to get the return code for an external program (in this case - php), however, the command's output is still printed to the screen.
How do I prevent it from doing so?
My code is:
use strict; use warnings;
print 'Return code:', system('php -l wrong.php'), "\n";
This code does print the return code, but it also print the the output of the command executed.
Any help will be appreciated!
EDIT: further testing shown that this happens while only using the php lint command.. using it with other commands doesnt print anything...
What you want is IPC::Open3:
use IPC::Open3;
use Symbol qw(gensym);
my $err = gensym; #work around IPC::Open3's interface
my $pid = open3 my $wtr, my $rdr, $err,
'some cmd', 'arg1', 'arg2', ...;
The separates out stdin into $wtr, stdout into $rdr, and stderr into $err
This also gives you maximum control over communicating with the process
If you are on a UNIX-like OS, you should be able to redirect the output in the command:
Try:
system('php -l wrong.php >> /dev/null') to get rid of what's being sent to stdout.
You could also open the command with a pipe to handle the output directly, which should be more portable.

How can I redirect output of die function to a file in Perl?

I want to redirect the die messages to a separate file so that I can compare that file later to determine what went wrong.
But this code gives me errors:
$ cat test.pl
use strict;
use warnings;
my $log = "msglog.log";
die $log "DEAD$!";
$ perl test.pl
Missing comma after first argument to die function at test.pl line 5, near ""DEAD$!";"
Execution of test.pl aborted due to compilation errors.
$
I do not want to do a 2> from the caller. Is there someway to redirect them from within the script?
You can install a $SIG{__DIE__} handler to be run just before the "die" runs. The handler will be called with the error message which you can log:
local $SIG{__DIE__} = sub {
my ($message) = #_;
# log the message
};
See $SIG{expr} in perlvar for details.
Perl's die prints to STDERR so you could redirect STDERR to a file.
#!/usr/bin/env perl
# the path above depends on your system
open(STDERR, ">>", "errlog.log");
die "Hello";
The Log::Log4perl module offers more than a few options.
One can choose to output the error message to both STDERR and the logfile.
my $logger = Log::Log4perl->init ( 'log.conf' );
# Configuration file controls what to output, like time, line number, class...
$logger->get_logger ( 'Hello::World' ); # Define which class logger to use
.
.
.
open my $fh, '<', $file
or $logger->logdie ( "Log the demise: $!" ); # ... and die;
While it requires a little bit more effort in terms of setup, its flexibility unlocks a great deal of potential.