刷新子进程的输出

时间:2019-02-05 15:53:51

标签: perl process output

我通过IPC :: Open2创建了一个子进程。
我需要逐行从此子进程的标准输出中读取。
问题是,由于子进程的stdout未连接到终端,因此它已完全缓冲,在进程终止之前我无法读取它。

如何在不修改其代码的情况下刷新子进程的输出?


子进程代码

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

父流程代码:

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";
}

运行代码时,在等待child_process的输出时卡住了
但是,如果我用 bc 运行它,结果就是我所期望的,我相信bc必须手动刷新其输出

注意:
在子进程中,如果我在打印开始时添加$| = 1或在打印后添加STDOUT->flush(),则父进程可以从中正确读取。
但是,这是一个示例,我必须处理不手动刷新其输出的程序。

2 个答案:

答案 0 :(得分:3)

不幸的是,Perl无法控制它所执行程序的缓冲行为。某些系统具有unbuffer实用程序可以执行此操作。如果您有权使用此工具,则可以说

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

有关here的Windows等效工具的讨论,但我不能说其中任何一个是否有效。

答案 1 :(得分:2)

一种方法是为进程设置类似于终端的环境,即伪终端(pty)。这很难做到,并且非常依赖于系统,但是IPC::Run具有易于使用的功能。

这里是驱动程序,使用at工具运行,使其没有控制终端(或通过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

使用>pty>,它为STDOUT中程序的@cmd设置了一个伪终端(这是带有>的管道);另请参见<pty<和更多about redirection。 每当孩子的输出时,都会调用匿名sub {},因此人们可以随时处理它。还有其他选择。

名为(t_term.pl)的程序仅对终端进行测试

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

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

在此示例中,-t STDOUT(请参阅filetest operators)是一种检查终端的合适方法。有关更多/其他方式,请参见this post

输出显示,即使驱动程序在没有驱动程序的情况下(使用t_term.pl或驱动程序用完了),被调用程序(STDOUT)确实在其at上看到了一个终端。 crontab)。如果使用>pty>(使用管道)将>更改为常规重定向,则没有端子。

这是否能解决缓冲问题显然取决于该程序,以及是否足以用终端欺骗它。

解决该问题的另一种方法是,尽可能使用unbuffer,就像暴民的答案一样。