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
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.