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

Hi, I'm trying to flush two streams (STDOUT, STDERR) so that their content are printed directly as the program uses these streams. However, my results are incorrect. I have 2 applications, where one is the core and the second is the executed.

UPDATE: it seems that my problem is when the process creating is used in a library so that both the Core application is using it and the 'second application' is using it.

Core:

use Library; $| = 1; Library::createProcess('second application');
Application:
use Library; select((select(STDOUT), $|=1)[0]); print STDOUT "Out \n"; print STDERR " -> Err \n"; print STDOUT "Out \n"; print STDERR " -> Err \n"; print STDOUT "Out \n"; print STDERR " -> Err \n"; Library::createProcess('random application');
Library:
use IO::Select; use IPC::Open3; sub createProcess($path) { my $pid = open3(*CMD_IN, *CMD_OUT, *CMD_ERR, $path); my $selector = IO::Select->new(); $selector->add(*CMD_ERR, *CMD_OUT); _parseStreams($selector); close(CMD_IN) or die "Unable to close STDIN: $!"; close(CMD_OUT) or die "Unable to close STDOUT: $!"; close(CMD_ERR) or die "Unable to close STDERR: $!"; waitpid($pid, 0) or die "Waitpid failed: $!"; } sub _parseStreams ($) { my($selector) = @_; my(@ready, $fh, $line); while(@ready = $selector->can_read()) { foreach $fh (@ready) { if(fileno($fh) == fileno(CMD_ERR)) { $line = scalar <CMD_ERR>; print STDERR $line; } else { $line = scalar <CMD_OUT>; print STDOUT $line; } $selector->remove($fh) if eof($fh); } } }
OUTPUT BECOMES:
Out Out Out -> Err -> Err -> Err

Replies are listed 'Best First'.
Re: [UPDATED] How to autoflush streams (i.e. open3 calls)
by BrowserUk (Patriarch) on Jan 29, 2009 at 12:51 UTC

    Have you tried using the same handle for both STDOUT and STDERR?

    Alternatively, if you're using win32, there is an system api to control the buffer size (local and remote) of pipes. You may be able to reduce the buffer size to 0 and so achieve some level of immediacy.


    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.
Re: How to autoflush streams (i.e. open3 calls)
by salva (Canon) on Jan 29, 2009 at 11:21 UTC
    You have set autoflush for STDOUT but not for STDERR, so, just do it:
    select(STDERR); $| = 1; select(STDOUT); $| = 1;
      are not STDERR automatically flushed?
        oops, yes you are right!

        The actual problem probably is that when the parent goes to read, the children has already finished and all the data is awaiting on the pipes buffers. The parent reads then first all the data from child STDOUT and then the data from child STDERR.

        AFAIK there is no way to synchronize child writes with parent reads.

        If you really need to know the order in with data is printed, use the same pipe for STDOUT and STDERR and preppend every line with a channel identifier so you can demultiplex it on the parent process.

      perl -MIO::Handle -e ' STDOUT->autoflush(1); print 1; sleep 1; print 2 + '
        But STDERR seems to be autoflushed by default:
        perl -MIO::Handle -e ' print 1; print STDERR 33; sleep 1; print 2';