为什么Perl的IO :: Pipe异常与eval块中的croak或die有不同的表现?

时间:2017-03-04 03:44:17

标签: perl exception-handling eval die

我在我的程序中注意到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!而没有错误消息。

当询问错误并运行croakdie路径时,它的行为也符合我的预期:捕获,报告异常并终止程序。

$ 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;消息但不终止?第二个问题是如何防止这种情况发生?如果程序无法运行,我希望程序停止执行。

1 个答案:

答案 0 :(得分:3)

在感兴趣的情况下,在eval之后运行两个进程。您可以通过在if ($@)之前添加打印语句来查看此内容。一个通过eval,然后到达最后die

与参数一起使用时reader分叉,以打开进程。当父进程返回pid时,该进程为exec。此代码位于_doit internal subroutine

如果这会导致孩子croak失败并显示您收到的消息。但是父母返回,因为它没有孩子的IPC,预计只会通过exec消失。所以父母逃脱并沿着eval向下走。该流程没有$@并绕过if ($@)

在使用reader打开进程的情况下,这似乎是错误处理的漏洞。

有办法解决这个问题。 $pipeIO::Handle,我们可以检查它并退出该额外流程,如果它不好(但简单$pipe->error在两种情况下都相同)。或者,由于涉及close,我们可以转到$?,当错误发生时确实为非零

# ...
$pipe->close;
exit if $? != 0;

(或者首先检查它)。这仍然是一个&#34;修复,&#34;这可能并不总是奏效。探测$pipe或查找逃逸者的PID的其他方法有点模糊(或者更糟糕的是,深入研究类内部)。

另一方面,从程序中收集输出和退出代码的简单方法是使用模块。一个不错的选择是Capture::Tiny。还有其他内容,例如IPC::RunIPC::Run3,或核心,而是低级IPC::Open3

鉴于澄清,正常open也应该足够。