简体   繁体   中英

Flush output of child process

I created a child process via IPC::Open2 .
I need to read from the stdout of this child process line by line.
Problem is, as the stdout of the child process is not connected to a terminal, it's fully buffered and I can't read from it until the process terminates.

How can I flush the output of the child process without modifying its code ?


child process code

while (<STDIN>) {
    print "Received : $_";
}

parent process code:

use IPC::Open2;
use Symbol;

my $in = gensym();
my $out = gensym();

my $pid = open2($out, $in, './child_process');

while (<STDIN>) {
    print $in $_;
    my $line = <$out>;
    print "child said : $line";
}

When I run the code, it get stucks waiting the output of the child process .
However, if I run it with bc the result is what I expect, I believe bc must manually flush its output

note:
In the child process if I add $| = 1 $| = 1 at the beginning or STDOUT->flush() after printing, the parent process can properly read from it.
However this is an example and I must handle programs that don't manually flush their output.

Unfortunately Perl has no control over the buffering behavior of the programs it executes. Some systems have an unbuffer utility that can do this. If you have access to this tool, you could say

my $pid = open2($out, $in, 'unbuffer ./child_process');

There's a discussion here about the equivalent tools for Windows, but I couldn't say whether any of them are effective.

One way is to set up a terminal-like environment for the process, a pseudo-terminal (pty). That is hard to do right and is very system dependent, but IPC::Run has that capability ready for easy use.

Here is the driver, run using at facility so that it has no controlling terminal (or run it via cron )

use warnings;
use strict;
use feature 'say';

use IPC::Run qw(run);

my @cmd = qw(./t_term.pl input arguments); 

run \@cmd, '>pty>', sub { say "out: @_" };

#run \@cmd, '>', sub { say "out: @_" }   # no pty

With >pty> it sets up a pseudo-terminal for STDOUT of the program in @cmd (it's a pipe with > ); also see <pty< and see more about redirection . The anonymous sub {} gets called every time there is output from the child, so one can process it as it goes. There are other options for this as well.

The program that is called ( t_term.pl ) only tests for a terminal

use warnings;
use strict;
use feature 'say';

say "Is STDOUT filehandle attached to a terminal: ",
    ( (-t STDOUT) ? "yes" : "no" );
sleep 2;
say "bye from $$";

The -t STDOUT (see filetest operators ) is a suitable way to check for a terminal in this example. For more/other ways see this post .

The output shows that the called program ( t_term.pl ) does see a terminal on its STDOUT , even when a driver runs without one (using at , or when run out of a crontab ). If the >pty> is changed to the usual redirection with > (using a pipe) then there is no terminal.

Whether this solves the buffering problems is clearly up to that program, and whether it is enough to fool it with a terminal.

Another way around the problem is using unbuffer when possible, as in mob's answer.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM