Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

Getting a return code from waitpid in Windows

by Anonymous Monk
on Jan 20, 2014 at 17:29 UTC ( #1071355=perlquestion: print w/replies, xml ) Need Help??
Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

I've been using system(1,"cmd...") to run multiple system commands in parallel on Windows 2008 R2 using perl 5.8.3 but have also tried 5.8.8 with the same results. My issue is acquiring the return code. If I setup a purposely bad command and just use my $rc = system("cmd...") I get a proper error code returned. When I do the following:
my $pid = system(1,"cmd..."); do { $wait = waitpid($pid,WNOHANG); } while ($wait != -1);
This works just fine but after the process terminates and waitpid returns a -1 indicating it has terminated I can't find any way to get the return code from original system command. The waitpid command returns -1 once it terminates regardless of whether it terminated gracefully or not. I've tried using $? but it just returns -1 and even if it didn't I'm not sure how it would distinguish between the multiple processes running in parallel that can be terminating at any time. Is there any way to retrieve the return code from the original system command, maybe by passing the PID? Is there a better way to do this that can both let me run multiple system commands in parallel on Windows and provide some error checking? Ultimately I just want to be able to output to the user which commands finished successfully and which errored. Thanks for any help and please let me know if you need any more information or if I've left anything out.

Replies are listed 'Best First'.
Re: Getting a return code from waitpid in Windows
by kschwab (Priest) on Jan 20, 2014 at 17:59 UTC
    From the perlvar docs:
    The status returned by the last pipe close, backtick (``) command, successful call to wait() or waitpid(), or from the system() operator. This is just the 16-bit status word returned by the traditional Unix wait() system call (or else is made up to look like it). Thus, the exit value of the subprocess is really ($? >> 8), and $? & 127 gives which signal, if any, the process died from, and $? & 128 reports whether there was a core dump. Additionally, if the h_errno variable is supported in C, its value is returned via $? if any gethost*() function fails. If you have installed a signal handler for SIGCHLD, the value of $? will usually be wrong outside that handler.
      As stated in the original post $? works when using system("cmd...") but when using system(1,"cmd...") with waitpid it always returns -1 whether the system command was successful or not. It seems that it's returning the return code of the waitpid command itself and not the process I was waiting on.

      Here is the full code example in case I'm just doing something stupid:
      my $wait = 0; my $pid = system(1,"cmd..."); do { $wait = waitpid($pid,WNOHANG); } while ($wait != -1); $rc = $?; print "Command completed with exit code $wait with return code $rc.\n" +;
      The output is "Command completed with exit code -1 with return code -1".

      If I use the same (purposely bad) command with system("cmd...") then $? returns a value of 256 which is what I would expect when it failed and what I would like to get out of the system(1,"cmd...") call.

      Thanks again.

        You call waitpid after you've reaped the process, causing causing an error ($? = -1;, error message in $!).

        You gotta check $? after the successful call to waitpid.

        use POSIX qw( WNOHANG ); my $pid = system(1,'perl -e"sleep(4); exit(5);"'); while (1) { my $wait = waitpid($pid, WNOHANG); die $! if $wait < 0; last if $wait > 0; } print "Command completed with exit code $?.\n";

        I presume you actually do something else in the loop? If not, there's no reason to use WHOHANG.

        my $pid = system(1,'perl -e"sleep(4); exit(5);"'); waitpid($pid, 0); die $! if $? < 0; print "Command completed with exit code $?.\n";
        I may have answered my own question... I still can't figure out how to get it to work with waitpid but it does work properly when using just plain "wait". The reason I wasn't doing that before is that it is a blocking call so it prevents me from looping through checking each PID I kicked off previously. However, it seems that it blocks until any of the processes returns. The wait call itself returns the PID that just terminated and just after $? returns the status. So presumably I can collect all my PIDs as I kick them off then loop through on a blocking wait one by one waiting for each of those PIDs to terminate and collect the status as they do.

        The following seems to do what I want:

        my $pid1Complete = 0; my $pid2Complete = 0; my $pid3Complete = 0; my $pid1 = system(1,"cmd..."); my $pid2 = system(1,"cmd..."); my $pid3 = system(1,"cmd..."); my $finishingPID = 0; do { $finishingPID = wait(); $rc = $?; if ($finishingPID == $pid1) { $pid1Complete = 1; print "PID1 finished with return code $rc\n"; } elsif ($finishingPID == $pid2) { $pid2Complete = 1; print "PID2 finished with return code $rc\n"; } elsif ($finishingPID == $pid3) { $pid3Complete = 1; print "PID3 finished with return code $rc\n"; } } while (($pid1Complete == 0) || ($pid2Complete == 0) || ($pid3Complet +e == 0));

        If anybody has any suggestions for improvements or other ideas I'd be happy to hear them.

        Thanks again.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1071355]
Approved by davido
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2017-05-30 04:10 GMT
Find Nodes?
    Voting Booth?