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

I am trying to write a simple helper script that will call three external programs multiple times (based on some parsed data). My problem is that Perl's system() call is returning before the command finishes. I have looked at the program I'm calling and it is actually a Perl script that calls additional programs (but this script can NOT be changed). The system() call returns while these programs execute in the background. The called program prints a final status message that I thought I could capture with my script to ensure that the system() call completed. Below is the snippet of the called program (that I can't edit) showing the final status message:
if ($exit_status != 0) { print("-F-Job completed abnormally.(status = $exit_status)\n"); } else { print("-I-Job completed successfully.\n"); }
As you can see, this is a simple print statement. After various attempts I am unable to capture this output. Below is my code snippet intended to capture STDOUT and STDERR and print each line. In my final program I will change this code to wait until I see either the success or failure message.
$command = "program_to_run $opt{c} -f test"; # print "$command\n"; open(RUN, "$command 2>&1|"); while (<RUN>) { print "$_\n"; next; } close(RUN); exit 0;
When I uncomment the print $command line it prints the correct system command. However, the print statements from that system command are not being captured. Am I doing something wrong? Thanks in advance for any tips you can provide. Greg

Replies are listed 'Best First'.
Re: Unable to capture STDOUT from system command
by almut (Canon) on Jun 29, 2010 at 20:42 UTC
    ... The system() call returns while these programs execute in the background.

    If the programs - as it seems - actually do run in the background, the problem is that the initial process (which you start via system or open) will fork another process and terminate itself, thereby also closing the pipe.  And as the command (parent process) has terminated, the respective Perl function will return more or less immediately.

    In case you're on Unix you should be able to use a named pipe (aka fifo):

    #!/usr/bin/perl my $command = "./program_to_run.pl"; my $fifo = "tmp.fifo"; system "mkfifo $fifo; $command >$fifo 2>&1 &"; open my $fh, "<", $fifo or die "Couldn't open fifo '$fifo': $!"; while (<$fh>) { print ": $_"; } close $fh; unlink $fifo;

    The background script (program_to_run.pl):

    #!/usr/bin/perl my $pid = fork(); if ($pid) { exit; } else { $| = 1; for (1..3) { print "foo"; print STDERR "bar\n"; sleep 1; } print "done.\n"; }

    Output (from the first script):

    : foobar : foobar : foobar : done.
Re: Unable to capture STDOUT from system command
by BrowserUk (Patriarch) on Jun 29, 2010 at 20:53 UTC

    Command line redirection is enacted by command line processors (shells). Probably all you need to do is prefix your command with an appropriate shell command for your system (eg. cmd.exe or sh or bash etc.).


    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Thanks to both of you! I had a mix of both issues; my one command was aliased in my shell (fortunately it was a $ENV variable that I could grab) and the code snippet worked perfectly for the rest.
Re: Unable to capture STDOUT from system command
by dasgar (Priest) on Jun 30, 2010 at 21:04 UTC
    Depending on what you're wanting to accomplish, you can go with a "quick, dirty" method (or at least that's how I think about it). You can use back ticks to execute a command and capture the STDOUT from the executed command.

    For example, the code below runs the cat command on the /proc/cpuinfo file and stores the STDOUT in the variable $procinfo.

    my $procinfo = `cat /proc/cpuinfo`;

    There are two potential problems with this method. First, this will cause your Perl script to wait until the called command is done before continuing. Second, I'm unaware of how to use this method to get STDERR data (unless you're piping STDERR to STDOUT inside of the back ticks, but I haven't tried that myself, so warranties or guarantees offered on that suggestion).

    Anyways, thought you might like to know of an alternative method that might work for you that involves fewer lines of code.
Re: Unable to capture STDOUT from system command
by Proclus (Beadle) on Jul 01, 2010 at 09:06 UTC
    I have bitten by this quite a few times before, so let me just share my experiences.

    I tend to use POE::Wheel::Run. It may not work properly in Windows especially if you're inside a GUI loop.(WxWidgets, Tk, Win32,...) Still worth a try. It has a beautiful interface and in most cases it will not block your program.

    Another solution I have applied in the past came from BrowserUK. This is using the notorious threads but BrowserUK transforms it into a true work of art:
    http://www.perlmonks.org/?node_id=621127