我在我的程序中注意到IO :: Pipe引发的异常表现得很奇怪,我无法弄清楚它在做什么(更不用说它是怎么做的)。我把它归结为一个简单的示例程序:
use strict;
use warnings;
use Carp;
use IO::Pipe;
my($path) = shift;
my($bad) = shift || "";
eval {
if ($path =~ m{pipe}i) {
my($bin) = ($bad ? "/bin/lsddd" : "/bin/ls");
my($pipe) = IO::Pipe->new();
$pipe->reader("$bin -l .");
print "$_" while <$pipe>;
$pipe->close;
}
elsif ($path =~ m{croak}i) {
croak "CROAKED" if $bad;
}
else {
die "DIED" if $bad;
}
};
if ($@) {
my($msg) = $@;
die "Caught Exception: $msg\n";
}
die "Uh-oh\n" if $bad;
print "Made it!\n";
示例程序有两个参数,一个用于指示在eval
块内部的代码路径,第二个用于指示是否生成错误(任何评估为false的内容都不会生成错误错误)。当没有请求错误时,所有三条路径都按预期运行;它们都打印Made it!
而没有错误消息。
当询问错误并运行croak
或die
路径时,它的行为也符合我的预期:捕获,报告异常并终止程序。
$ perl example.pl die foo
Caught Exception: DIED at example.pl line 23.
和
$ perl example.pl croak foo
Caught Exception: CROAKED at example.pl line 11.
eval {...} called at example.pl line 10
但是,当我向IO :: Pipe路径发送错误时,它报告错误,但程序继续执行,直到到达外部die
:
$ perl example.pl pipe foo
Caught Exception: IO::Pipe: Cannot exec: No such file or directory at example.pl line 15.
Uh-oh
第一个问题是为什么 - 为什么程序会报告&#34; Caught Exception&#34;消息但不终止?第二个问题是如何防止这种情况发生?如果程序无法运行,我希望程序停止执行。
答案 0 :(得分:3)
在感兴趣的情况下,在eval
之后运行两个进程。您可以通过在if ($@)
之前添加打印语句来查看此内容。一个通过eval
,然后到达最后die
。
与参数一起使用时reader
分叉,以打开进程。当父进程返回pid时,该进程为exec
。此代码位于_doit
internal subroutine
如果这会导致孩子croak
失败并显示您收到的消息。但是父母返回,因为它没有孩子的IPC,预计只会通过exec
消失。所以父母逃脱并沿着eval
向下走。该流程没有$@
并绕过if ($@)
。
在使用reader
打开进程的情况下,这似乎是错误处理的漏洞。
有办法解决这个问题。 $pipe
是IO::Handle,我们可以检查它并退出该额外流程,如果它不好(但简单$pipe->error
在两种情况下都相同)。或者,由于涉及close,我们可以转到$?
,当错误发生时确实为非零
# ...
$pipe->close;
exit if $? != 0;
(或者首先检查它)。这仍然是一个&#34;修复,&#34;这可能并不总是奏效。探测$pipe
或查找逃逸者的PID的其他方法有点模糊(或者更糟糕的是,深入研究类内部)。
另一方面,从程序中收集输出和退出代码的简单方法是使用模块。一个不错的选择是Capture::Tiny。还有其他内容,例如IPC::Run和IPC::Run3,或核心,而是低级IPC::Open3。
鉴于澄清,正常open
也应该足够。