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

This seems so simple, yet I can't find a solution that doesn't make my head hurt...

How can I capture STDOUT and STDERR of a child program in Win32 -- without letting cmd.exe get its grubby hands on my parameters, and sticking to the stuff in Perl core? (Since installing additional stuff everywhere isn't an option in this case, the core requirement rules out IPC::Run.)

The my $foo = `command` and system() approach (apparently) pass through cmd.exe, which breaks up parameters with spaces, not to mention the ugliness with non-7bit-ASCII characters. Apparently the construction documented under "Safe Pipe Opens" in PerlIPC and that I normally use, open(FH, '-|'), doesn't work on Win32. (It complains that - isn't a recognized command.) I looked at Win32::Process, which creates a process fine but doesn't seem to let me redirect the child's file handles.

So... how then to capture the child process' output?

I need to get its exit code too, but I assume that can be managed with $? and/or $proc_handle->GetExitCode().)
  • Comment on capture child process STDOUT/STDERR on Win32

Replies are listed 'Best First'.
Re: capture child process STDOUT/STDERR on Win32
by moritz (Cardinal) on Jul 28, 2008 at 10:11 UTC
    If IPC::Run does what you need, look at how it does that, and re-implement it (or just plain copy & paste, as AnonMonk suggested; but make sure to check its license first).

    Reimplementing the wheel is bad enough, no need to redesign it as well.

Re: capture child process STDOUT/STDERR on Win32
by eyepopslikeamosquito (Archbishop) on Jul 28, 2008 at 12:25 UTC

    Some sample code that runs an exe without invoking a shell, capturing the command return code, $rc, and the command stdout and stderr folded into a file, ff.tmp:

    use strict; use warnings; my $outfile = 'ff.tmp'; my $exe = $^X; my @args = ( 'perl', '-e', 'print qq{to stdout};print STDERR qq{to +stderr}' ); print "here we go, running '$exe'\n"; print STDERR "here we go to stderr\n"; open(SAVOUT, ">&STDOUT") or die "error: save original STDOUT: $!"; open(SAVERR, ">&STDERR") or die "error: save original STDERR: $!"; open(STDOUT, '>', $outfile) or die "error: create '$outfile': $!"; open(STDERR, '>&STDOUT') or die "error: redirect '$outfile': $!"; system { $exe } @args; my $rc = $? >> 8; open(STDOUT, ">&SAVOUT") or die "error: restore STDOUT: $!"; open(STDERR, ">&SAVERR") or die "error: restore STDERR: $!"; close(SAVERR) or die "error: close SAVERR: $!"; close(SAVOUT) or die "error: close SAVOUT: $!"; print "rc=$rc\n"; print STDERR "rc=$rc to STDERR\n";

    Should work fine on both Windows and Unix.

      I'm so used to packages being wrapped DLLs it didn't even occur to me that I could just copy and paste the code. I'll look into that.

      I'd thought of juggling stderr and stdout to take advantage of the fact children inherit those file handles, but I'd really like a cleaner way. Not having open(FH, '-|') on Win32 seems like a major oversight, so I was hoping there was a trivial work-around I just didn't know about.

      Thanks all.
Re: capture child process STDOUT/STDERR on Win32
by pjotrik (Friar) on Jul 28, 2008 at 10:04 UTC
    I can't answer your question as a whole, I don't have much experience with perl on Windows. But you mention that cmd.exe clobbers your parameters - enclosing them in double quotes should prevent that.
Re: capture child process STDOUT/STDERR on Win32
by Anonymous Monk on Jul 28, 2008 at 09:25 UTC
    (Since installing additional stuff everywhere isn't an option in this case, the core requirement rules out IPC::Run.)
    copy and paste
Re: capture child process STDOUT/STDERR on Win32
by Bloodnok (Vicar) on Jul 28, 2008 at 11:48 UTC
    Hi ,

    I seem to recall, from a project in the not too distant past, that we had a similar mountain to climb using Strawberry Perl 5.8.8.
    IIRC, since perl (c/w the shell) handles any redirection, the problem was easily resolved using open() in conjunction with standard *NIX redirection (ignoring '-' as a stdin/stdout designator) e.g.

    open CMD, "command 2>&1 |" or die "Couldn't open() command - $!"; while (<CMD>) { . .

    HTH,

    At last, a user level that overstates my experience :-))