Perl:如果 IPC::Run 终止,则从进程中检索输出

标签 perl error-handling

我一直在使用 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相对应的进程的STDOUTSTDERR ,不是行缓冲,而是 block 缓冲。因此,所有输出都收集在缓冲区中,直到缓冲区已满或显式刷新时才显示(打印)。但是,当您的命令超时时,所有输出仍在缓冲区中,尚未刷新,因此收集到父进程(脚本)中的变量 $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/

相关文章:

perl - 在 "print"语句的开头加点?

python - 如何使用 python 提取 .gpx 数据

mysql - DBI/DBD::mysql/mysql_type_name: 如何区分Blob和Text?

sql - 在执行 postgresql 函数时返回错误值的语法

perl - Perl 中的 'my' 和 'our' 有什么区别?

perl - 我们可以加载没有 ".pm"文件扩展名或其他文件扩展名的 perl 模块吗?

C++ 使用枚举来处理错误

php - Codeigniter 和 PHP - 强制 404?

python - Mechanize 时不时地随机出错?

java - 如何从 JAR 调用方法而不面临依赖性问题? java