我一直使用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个要多。
答案 0 :(得分:1)
如@ysth所述,您没有获得任何输出的原因是,与命令STDOUT
对应的进程的STDERR
和$cmd
不是行缓冲的,而是阻止缓冲。因此,所有输出都收集在缓冲区中,该缓冲区未显示(打印),直到缓冲区已满或明确刷新为止。但是,当您的命令超时时,所有输出仍然在缓冲区中并且尚未刷新,因此被收集到父进程(脚本)中的变量$out
中。
另请注意,由于您的$cmd
脚本是Perl脚本,因此perlvar
中记录了此行为:
$ |
如果设置为非零,则在每次写入后立即强制刷新 或在当前选定的输出通道上打印。默认值为0 (无论通道是否真的由系统缓冲或 不; $ |只告诉您是否已明确要求Perl刷新 每次写完后)。 如果输出为STDOUT,通常会进行行缓冲 到终端和阻止缓冲否则。
问题(程序未连接到终端或tty)也在IPC::Run
的文档页面中注明:
交互式应用程序通常针对人类使用进行优化。这个可以 帮助或阻碍尝试通过类似的模块与他们互动 IPC ::运行。程序通常会在检测到时改变其行为 假设,stdin,stdout或stderr没有连接到tty 它们以批处理模式运行。这是帮助还是伤害 取决于哪些优化会发生变化。而且往往没办法 告诉我们程序在这些方面做了什么,而不是试错 偶尔,阅读来源。这包括不同的版本 和相同程序的实现。
该文档还列出了一组可能的解决方法,包括使用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)
这样的语句,而是可以欺骗脚本以确保它连接到终端,因此它应该使用行缓冲。对于您的情况,我们只需在调用>pty>
的{{1}}参数之前添加\$out
参数:
harness