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

Hi, I'm trying to run a command on a unix box using system because I need the exit code. but I also need the stdout and stderr. so I try some STDOUT redirection unfortunatelly it works if I redirect to a file:

open(STDOUT, '>' ,"kk.txt" ) or die "Can't redirect stdout: $!";

But not when I do it to a variable:

open(STDOUT, '>' ,\$output ) or die "Can't redirect stdout: $!";

This is my Testing code:

#!/usr/bin/perl my $exitcode ; my $output=''; my $error=''; # take copies of the file descriptors open(OLDOUT, ">&STDOUT"); open(OLDERR, ">&STDERR"); #close current outs as per manual of open close(STDOUT) or die "Can't close STDOUT: $!"; close(STDERR) or die "Can't close STDERR: $!"; # redirect stdout and stderr open(STDOUT, '>' ,\$output ) or die "Can't redirect stdout: $!"; #open(STDOUT, '>' ,"kk.txt" ) or die "Can't redirect stdout: $!"; open(STDERR, '>' ,\$error ) or die "Can't redirect stderr: +$!"; printf "Before system\n"; # run the program system("echo I cant get this into a variable"); $exitcode=($? >>8); printf "After System\n"; # close the redirected filehandles close(STDOUT) or die "Can't close STDOUT: $!"; close(STDERR) or die "Can't close STDERR: $!"; # restore stdout and stderr open(STDOUT, ">&OLDOUT") or die "Can't restore stdout: $!"; open(STDERR, ">&OLDERR") or die "Can't restore stderr: $!"; # avoid leaks by closing the independent copies close(OLDOUT) or die "Can't close OLDOUT: $!"; close(OLDERR) or die "Can't close OLDERR: $!"; printf "Exitcode: %d\n" ,($exitcode); printf "still here\n"; print $output ; print $error ;

Please any help on why it does not work the redirection when is a variable?

Also if posible. how can I do this without system. remember I need exitcode stdout and stderr and I do not want to do any alter to the actual command to do unix redirections.

Many many thanks.

Guybrush.

Replies are listed 'Best First'.
Re: system stdout redirected ok to a file but not to a variable.
by salva (Canon) on May 28, 2015 at 21:18 UTC
    Redirection to/from a scalar file handle (any tie'ing file handle actually) does not work for external processes because those perl handlers are not backed by a real file handle at the operating system level.

    You can use backticks to capture just one of the subprocess output streams. Or IPC::Open3, and select or any module allowing to handle IO events to read all the streams concurrently, or even some module doing the capturing for you.

Re: system stdout redirected ok to a file but not to a variable.
by graff (Chancellor) on May 28, 2015 at 23:03 UTC
    In case the "Capture::Tiny" thing suggested above doesn't work for you, the next thing I'd suggest is to just ditch all the manipulations of STDOUT and STDERR in the perl script, add suitable redirection tokens to the shell command that you pass to system(), and then open and read the files that were created by the system call:
    my $cmdout = "/tmp/cmd.stdout.$$"; my $cmderr = "/tmp/cmd.stderr.$$"; system( "my_special_command > $cmdout 2> $cmderr" ); my $exitcode = ( $? >> 8 ); for my $log ( $cmdout, $cmderr ) { open( my $logfh, "<", $log ) or die "WTF? $!"; local $/; my $logdata = <$logfh>; # now what? } unlink $cmdout, $cmderr;
    (not tested)
Re: system stdout redirected ok to a file but not to a variable.
by Anonymous Monk on May 28, 2015 at 22:27 UTC
    Capture::Tiny does that successfully
    #!/usr/bin/perl -- use strict; use warnings; use Capture::Tiny qw/ capture /; my @cmd = ( ); my($stdout, $stderr, $exit) = capture { system { $cmd[0] } @cmd; };;
Re: system stdout redirected ok to a file but not to a variable.
by GotToBTru (Prior) on May 28, 2015 at 21:08 UTC

    What specifically happens? What error message(s) do you get?

    Dum Spiro Spero
Re: system stdout redirected ok to a file but not to a variable.
by guybrush (Initiate) on May 29, 2015 at 07:31 UTC

    It's a pity to read that there is a logical explanation for what is happening :-(

    I'll try to go with the IPC::open3 option and see if I can control exit codes, and STDOUT / STDERR.

    The this is this script needs to be some kind of generic Wrapper for a monitoring tool. Therefore I'm not sure which modules/libraries I have available there. that's why I needed to have the simpliest version.

    Other possible solution might be to dump the stdout/stderr to a temp file and read it afterwards, but... did not want to "touch" anything on the server :-(

    Thanks.

      Don’t worry excessively about that.   Temporary files get created and deleted all the time, even when you are not aware of it, in the ordinary course of the computer’s daily activities.   (File::Temp might be handy here.)   If you happen to be working in the Windows environment, you may encounter some of the subtle differences between the two systems.   I also notice that the perldoc page for IPC::Open3 seems to suggest that the author thinks that IPC::Run is “better,” although I notice also that it is quite a bit more feature-laden.