Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

once again: program output and return code

by particle (Vicar)
on Jun 07, 2001 at 17:14 UTC ( #86540=perlquestion: print w/ replies, xml ) Need Help??
particle has asked for the wisdom of the Perl Monks concerning the following question:

this question has been asked and answered many times. but every answer has left me wanting something more. there must be something i'm missing, what is it?

i need to capture STDOUT, STDERR, and a return code from a 3rd party executable. i have code samples below of each method. i can't include the 3rd party exe, so i wrote a little perl code~

#!/usr/bin/perl # return.pl print STDERR "err1!\nerr2!\n"; print STDOUT "out!\nout again!\n"; exit 1;
i figured my best bet was IPC::Open3, but this does not give me the return code. even $? is set to the pid. here's a little code sample~
#!/usr/bin/perl # test1.pl use IPC::Open3; $n = "\n"; open( OUTPUT, ">&STDOUT" ) or die "Can't dup STDOUT to OUTPUT: $!$n"; open( OUTERR, ">&STDERR" ) or die "Can't dup STDERR to OUTERR: $!$n"; eval { $pid = open3("<&STDIN", \*OUTPUT, \*OUTERR, 'perl return.pl') } +; $@ && die "ERROR: $@$n"; @results = <OUTPUT>; @errors = <OUTERR>; close OUTPUT; close OUTERR; print "---pid$n"; print $pid . $n; print "---\$?$n"; print $? . $n; print "---results$n"; foreach(@results) { print $_ . $n }; print "---errors$n"; foreach(@errors) { print $_ . $n };
here's the output:
---pid 1480 ---$? 1480 ---results out! out again! ---errors err1! err2
as you can see, i can capture STDOUT and STDERR fine, but i can't find a way to get the exit code. $@ is the exit code from open3, should something disasterous happen. not the exit code from the executed code 'perl return.pl'.

i also tried system, to no avail~

#!/usr/bin/perl -w # test2.pl $n = "\n"; eval{ open( OUTPUT, "+>&STDOUT" ) or die "Can't dup STDOUT to OUTPUT: $!$n"; open( OUTERR, "+>&STDERR" ) or die "Can't dup STDERR to OUTERR: $!$n"; $return = system('perl', 'return.pl'); chomp ( @results = <OUTPUT> ); chomp ( @errors = <OUTERR> ); close OUTPUT; close OUTERR; }; $@ && die "ERROR: $@$n"; print "---return$n"; print $return>>8 , $n; print "---results$n"; foreach(@results) { print $_ . $n }; print "---errors$n"; foreach(@errors) { print $_ . $n };
and here's it's output:
err1! err2 out! out again! ---return 1 ---results ---errors
here i get the return code (GOOD!), but the output is not going being redirected. this looks more promising, but i'm tired so i've come for help.

the relevant links i found are listed below, for reference:

I want to capture a programs output AND I want the return status
Changing the name of STDOUT...
How can I redirect STDOUT and STDERR from a program on WIN32?
IPC::Open3
perlfunc:system

what am i missing?

=Particle

Comment on once again: program output and return code
Select or Download Code
Re: once again: program output and return code
by jeroenes (Priest) on Jun 07, 2001 at 17:42 UTC
    eval tells me:
    In both forms, the value returned is the value of the last expression evaluated inside the mini- program; a return statement may be also used, just as with subroutines. The expression providing the return value is evaluated in void, scalar, or list context, depending on the context of the eval itself. See the wantarray entry elsewhere in this document for more on how the evaluation context can be determined.
    So you can get the return with $ret = eval ....

    If you want to execute a shell, use system with redirection:

    $ret = system('perl','return.pl',">$tmp_stdout","2&>$tmp_stderr");
    Jeroen
    "We are not alone"(FZ)
      $@ is a shortcut to the return code from eval. and that i'm getting. it's the redirection that i can't figure out.

      i assume you meant double-quotes around $tmp_stdout and $tmp_stderr. i ran your corrected code on Win32, and i can't get this to work there.

      =Particle

        No, perlvar sayz:
        $@ The Perl syntax error message from the last eval() operator. If null, the last eval() parsed and executed correctly (although the operations you invoked may have failed in the normal fashion). (Mnemonic: Where was the syntax error "at"?) Warning messages are not collected in this variable. You can, however, set up a routine to process warnings by setting `$SIG{__WARN__}' as described below. Also see the Error Indicators entry elsewhere in this document.
        So that's something different (it returns the syntax error). use $ret=eval... for return values.

        And indeed, that redirection won't work under windows. But maybe a real shell helps in that respect: try cygwin or bash4win.

        Oh, and fixed those quotes. Sorry about that.

        Jeroen

      Adding an extra level of indirection with eval does noth solve the problem. eval returns the value of the last expression evaluated, but that last expression would return the same value without the help of eval:
      $x = 1; $y = 2; $return1 = $x + $y; $return2 = eval { $x = 1; $y = 2; $x + $y }
      If particle already knew what that last expression should be, to get the value he wants, using eval wouldn't gain him anything.

      I know how to get STDIN and STDOUT, or STDIN and the exit value, but I can't figure out how to get STDOUT, STDERR, and the exit value. :(

        > I know how to get STDIN and STDOUT, or STDIN and the exit value, but I can't figure out how to get STDOUT, STDERR, and the exit value. :(

        i'm just glad i'm not crazy. but maybe i can narrow the scope a little. i know i can get return code from system. but can i redirect the output of the system call in a portable way?

        =Particle

Re: once again: program output and return code
by particle (Vicar) on Jul 09, 2001 at 20:31 UTC
    tye helped me out with this code a while back on another thread, and i just wanted to finish this up with the working code, so here it is~
    #!/usr/bin/perl # test1.pl use IPC::Open3; $n = "\n"; open( OUTPUT, ">&STDOUT" ) or die "Can't dup STDOUT to OUTPUT: $!$n"; open( OUTERR, ">&STDERR" ) or die "Can't dup STDERR to OUTERR: $!$n"; eval { $pid = open3("<&STDIN", \*OUTPUT, \*OUTERR, 'perl return.pl') ; $val = waitpid(-1,0); # <--- added this line }; $@ && die "ERROR: $@$n"; @results = <OUTPUT>; @errors = <OUTERR>; close OUTPUT; close OUTERR; print "---pid$n"; print $pid . $n; print "---\$?$n"; print $? . $n; # <--- prints exit val print "---results$n"; foreach(@results) { print $_ . $n }; print "---errors$n"; foreach(@errors) { print $_ . $n };

    ~Particle

      I've recently had the same sort of conundrum on Win32 and Perl. I nifty trick I've seen is to use OS redirection to shove STDERR, to STDOUT, as most shells will do with "/bin/ls 2&>1" In that way you get ALL the output of the program, and you can still manage to obtain the exit code using $?
      This worked for me but I had to duplicate STDIN as well as the other two, pass the dup to open3 and close it afterwards if I wanted to call the subroutine with the open3 call more than once, else:
      Carp::croak('open3: close(main::STDIN) failed: Bad file number') cal +led at .../IPC/Open3.pm line 119 IPC::Open3::xclose('main::STDIN') called at .../IPC/Open3.pm line 23 +8 IPC::Open3::_open3('open3', 'main', '<&STDIN', 'GLOB(0xa8c04)', 'GLO +B(0xa8c88)', 'pmem 2914') called at .../IPC/Open3.pm line 249 IPC::Open3::open3('<&STDIN', 'GLOB(0xa8c04)', 'GLOB(0xa8c88)', 'pmem + 2914') called at ...

        you might find IPC::Run and IPC::Run3 of use. they make ipc much easier than the open* modules.

        ~Particle *accelerates*

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://86540]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others scrutinizing the Monastery: (17)
As of 2014-09-22 15:37 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    How do you remember the number of days in each month?











    Results (198 votes), past polls