views:

414

answers:

6

I have a cross-platform Perl program that starts a win32 windows program on win, and a macosx appliaction on the mac.

I use system(), which on the mac makes the stdout of the invoked program, be written in the stdout of the Perl program, which is what i want.

On Windows, it seems like there is no way to get the stdout a Windows program. So as an alternative, I'm having the program write to a logfile instead, and I'd like Perl to read from the logfile (as it's being written to, the invoked program could run for an hour), and redirect that back to Perl's stdout, so that the experience on Win and Mac is the same.

Does anybody know how to actually do this in Perl? I'm thinking:

  • fork the process
  • File::Tail the logfile, writing to stdout as data comes in
  • somehow figure out when the program actually finished running.

I can probably figure out #1 and #2, but don't know yet how to tackle #3.

+1  A: 

If you need to capture the output your programs print to STDOUT, why don't you simply use backticks instead of system()?

my $stdout = `program_name`;
if ( $? ) {
    print "Child process had an error";
}
innaM
A: 

Have you read the docs for system? system does not let you capture STDOUT. qx does.

On the other hand, it does not look like you really want to capture STDOUT. It seems like you want the output from the program to appear in the same terminal window as the Perl program. If the program you are invoking on Windows does print its output to STDOUT and if you are running your Perl program in a cmd window, then this will happen naturally.

Sinan Ünür
+2  A: 

IPC::Run3 lets you provide stuff for stdin and capture stdout and stderr.

hillu
+3  A: 

Would the following fulfill your original intention: stdout of win32 program is also stdout of perl (or did I misunderstand your original question)?

// Win32 console application printing to stdout
#include <stdio.h>

int main(int argc, char* argv[])
{
    int idx;
    for (idx=0; idx < 10; idx++) {
     printf("Hello World!\n");
     fflush(stdout);
    }
    return 0;
}

Perl program capturing stdout of windows program and redirecting to stdout:

use strict; use warnings;

my $cmd = "Debug/hello.exe";
open my $cmd_h, "$cmd |" or die "Cannot open pipe to '$cmd': $!";
print "Perl:$_" while <$cmd_h>;
close $cmd_h or die "Cannot close pipe to '$cmd': $!";
Cecil
A: 

Capture the return value of the fork call to get the process ID of the child.

my $pid = fork();
if ($pid == 0) { # child
    system("command > logfile");
    exit 0;
}
# else parent
do {
    # ... apply File::Tail to logfile, print new output ... 
while (process_is_active($pid));

Then there are lots of ways to tell when the child has finished. Here are two:

# 1. kill 0, $pid
sub process_is_active {
    my ($pid) = @_;
    return kill 0, $pid;  # returns "number of processes successfully signalled"
}

Some systems do a better job of implementing kill 0,... than others.

# 2. non-blocking waitpid 
sub process_is_active {
    use POSIX ':sys_wait_h';
    my ($pid) = @_;
    my $waitpid = waitpid $pid, WNOHANG;
    return $waitpid == $pid;
}

The second solution will only work once -- after waitpid detects that the process has finished and reaps it, it will return -1 if you call it again on that process.

mobrule
A: 

Not sure quite what you mean by "get the stdout", but this correctly prints "foo" to standard out when run using perl.exe from the Windows command line:

perl -e "system 'echo foo'"

So I think as long as you run Perl in a command window, and don't try to mess with any of the stdout filehandles before calling system it should work.

Dan