我通过IPC::Open2
创建了一个子进程。
我需要从这个子进程的stdout中逐行读取。
问题是,由于子进程的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";
}
当我运行代码时,它会阻塞等待子进程的输出。
但是,如果我使用bc
运行它,结果就是我所期望的,我相信bc
必须手动刷新其输出
注:
在子进程中,如果我在开始时添加$| = 1
或在打印后添加STDOUT->flush()
,父进程可以正确地从中读取。
然而,这是一个例子,我必须处理程序,不手动刷新他们的输出。
2条答案
按热度按时间ct2axkht1#
不幸的是Perl无法控制它所执行的程序的缓冲行为。
有一个关于Windows的等效工具的讨论,但我不能说它们中的任何一个是否有效。
dly7yett2#
处理缓冲的一种方法是为进程建立一个类似终端的环境,一个伪终端(pty),这通常不容易做到,但是IPC::Run已经准备好了这个功能。
下面是驱动程序,使用at工具运行测试,使其没有控制终端(或通过
cron
运行)对于
>pty>
,它为@cmd
中的程序的STDOUT
设置一个伪终端(对于>
,它是一个管道);也可以参见<pty<
和更多关于重定向的内容。每次有来自子进程的输出时都会调用匿名的sub {}
,这样就可以在它运行时对其进行处理。还有其他相关的选项。调用的程序(
t_term.pl
)仅测试终端-t STDOUT
(参见filetest operators)是检查本例中终端的合适方法。有关更多/其他方法,请参见this post。输出显示被调用的程序(
t_term.pl
)在它的STDOUT
上确实看到了一个终端,即使驱动程序在没有终端的情况下运行(使用at
,或者在crontab
之外)。如果>pty>
被更改为通常的重定向>
(管道),那么就没有终端。这是否能解决缓冲问题显然取决于那个程序,以及它是否足以用一个终端欺骗它。
另一种解决问题的方法是尽可能使用
unbuffer
,如mob的答案所示。