perl 刷新子进程的输出

gijlo24d  于 2022-11-15  发布在  Perl
关注(0)|答案(2)|浏览(212)

我通过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(),父进程可以正确地从中读取。
然而,这是一个例子,我必须处理程序,不手动刷新他们的输出。

ct2axkht

ct2axkht1#

不幸的是Perl无法控制它所执行的程序的缓冲行为。

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

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

dly7yett

dly7yett2#

处理缓冲的一种方法是为进程建立一个类似终端的环境,一个伪终端(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>,它为@cmd中的程序的STDOUT设置一个伪终端(对于>,它是一个管道);也可以参见<pty<和更多关于重定向的内容。每次有来自子进程的输出时都会调用匿名的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,如mob的答案所示。

相关问题