我通过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()
,则父进程可以从中正确读取。
但是,这是一个示例,我必须处理不手动刷新其输出的程序。
答案 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
,就像暴民的答案一样。