我一直在使用 IPC::Run 运行一些命令模块,一切都很好,除了我无法访问输出(STDOUT、STDERR)、生成的过程并被重定向到变量中。有没有办法在错误处理中检索这些内容?
@commands = ();
foreach my $id (1..3) {
push @commands, ["perl", "script" . $id . ".pl"];
}
foreach my $cmd (@commands) {
my $out = "";
my $err = "";
my $h = harness $cmd, \undef, \$out, \$err, timeout(12,exception => {name => 'timeout'});
eval {
run $h;
};
if ($@) {
my $err_msg = $@; # save in case another error happens
print "$out\n";
print "$err\n";
$h->kill_kill;
}
}
我现在不需要任何输入,我只需要执行它并获取输出。
编辑 我一直在运行 perl 脚本来测试它,如下所示:
for (my $i = 0; $i < 10; $i++) {
sleep 1;
print "Hello from script 1 " . localtime() . "\n";
}
我有 3 个这样的脚本,时间不同,第 3 个需要 20 秒才能完成,这比我计时器中的 12 秒还要多。
最佳答案
正如@ysth所指出的,您没有得到任何输出的原因是与命令$cmd相对应的进程的
,不是行缓冲,而是 block 缓冲。因此,所有输出都收集在缓冲区中,直到缓冲区已满或显式刷新时才显示(打印)。但是,当您的命令超时时,所有输出仍在缓冲区中,尚未刷新,因此收集到父进程(脚本)中的变量 STDOUT
和STDERR
$out
中。
另请注意,由于您的 $cmd
脚本是 Perl 脚本,因此此行为记录在 perlvar
中。 :
$|
If set to nonzero, forces a flush right away and after every write or print on the currently selected output channel. Default is 0 (regardless of whether the channel is really buffered by the system or not; $| tells you only whether you've asked Perl explicitly to flush after each write). STDOUT will typically be line buffered if output is to the terminal and block buffered otherwise.
IPC::Run
的文档页面中也指出了该问题(程序未连接到终端或 tty)。 :
Interactive applications are usually optimized for human use. This can help or hinder trying to interact with them through modules like IPC::Run. Frequently, programs alter their behavior when they detect that stdin, stdout, or stderr are not connected to a tty, assuming that they are being run in batch mode. Whether this helps or hurts depends on which optimizations change. And there's often no way of telling what a program does in these areas other than trial and error and occasionally, reading the source. This includes different versions and implementations of the same program.
该文档还列出了一组可能的解决方法,包括使用 pseudo terminals .
针对您的具体情况的一个解决方案是在脚本开头显式地缓冲 STDOUT
行:
STDOUT->autoflush(1); # Make STDOUT line buffered
# Alternatively use: $| = 1;
for (my $i = 0; $i < 10; $i++) {
sleep 1;
print "Hello from script 1 " . localtime() . "\n";
}
编辑:
如果由于某种原因无法修改正在运行的脚本,您可以尝试将脚本连接到伪终端。因此,您可以欺骗脚本,使其相信它已连接到终端,因此它应该使用行缓冲,而不是在脚本的源代码中插入诸如 STDOUT->autoflush(1)
之类的语句。对于您的情况,我们只需在调用 harness
时在 \$out
参数之前添加一个 >pty>
参数即可:
my $h = harness $cmd, \undef, '>pty>', \$out,
timeout(12, exception => {name => 'timeout'});
eval {
run $h;
};
关于Perl:如果 IPC::Run 终止,则从进程中检索输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50392512/