刷新子进程的输出
Flush output of child process
我通过 IPC::Open2
创建了一个子进程。
我需要逐行读取这个子进程的标准输出。
问题是,由于子进程的标准输出没有连接到终端,它是完全缓冲的,在进程终止之前我无法读取它。
如何在不修改子进程代码的情况下刷新子进程的输出?
子进程代码
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()
,父进程可以正确读取它。
然而,这是一个示例,我必须处理不手动刷新其输出的程序。
不幸的是,Perl 无法控制它执行的程序的缓冲行为。有些系统有一个 unbuffer
实用程序可以执行此操作。如果您有权使用此工具,您可以说
my $pid = open2($out, $in, 'unbuffer ./child_process');
有一个关于 Windows 的等效工具的讨论 here,但我不能说它们是否有效。
(尝试)处理缓冲的一种方法是为进程设置类似终端的环境,即伪终端 (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<
和更多 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) is a suitable way to check for a terminal in this example. For more/other ways see 。
输出显示被调用程序 (t_term.pl
) 在其 STDOUT
上确实看到了一个终端,即使驱动程序 运行 没有终端(使用 at
, 或出 crontab
)。如果 >pty>
更改为通常的重定向 >
(管道),则没有终端。
这是否解决了缓冲问题显然取决于该程序,以及是否足以用终端来愚弄它。
解决该问题的另一种方法是尽可能使用 unbuffer
,如暴徒的回答。
我通过 IPC::Open2
创建了一个子进程。
我需要逐行读取这个子进程的标准输出。
问题是,由于子进程的标准输出没有连接到终端,它是完全缓冲的,在进程终止之前我无法读取它。
如何在不修改子进程代码的情况下刷新子进程的输出?
子进程代码
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()
,父进程可以正确读取它。
然而,这是一个示例,我必须处理不手动刷新其输出的程序。
不幸的是,Perl 无法控制它执行的程序的缓冲行为。有些系统有一个 unbuffer
实用程序可以执行此操作。如果您有权使用此工具,您可以说
my $pid = open2($out, $in, 'unbuffer ./child_process');
有一个关于 Windows 的等效工具的讨论 here,但我不能说它们是否有效。
(尝试)处理缓冲的一种方法是为进程设置类似终端的环境,即伪终端 (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<
和更多 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) is a suitable way to check for a terminal in this example. For more/other ways see
输出显示被调用程序 (t_term.pl
) 在其 STDOUT
上确实看到了一个终端,即使驱动程序 运行 没有终端(使用 at
, 或出 crontab
)。如果 >pty>
更改为通常的重定向 >
(管道),则没有终端。
这是否解决了缓冲问题显然取决于该程序,以及是否足以用终端来愚弄它。
解决该问题的另一种方法是尽可能使用 unbuffer
,如暴徒的回答。