为什么Perl';s IO::管道异常的行为与评估块中的croak或die不同?

为什么Perl';s IO::管道异常的行为与评估块中的croak或die不同?,perl,exception-handling,eval,die,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

我在程序中注意到,从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";

但是,当我沿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
第一个问题是为什么——为什么程序报告“捕获的异常”消息而不终止?第二个问题是如何防止这种情况发生?如果程序无法运行,我希望程序停止执行。

在感兴趣的情况下,有两个进程在
eval
之后运行。您可以通过在
if($@)
之前添加print语句来看到这一点。一个通过
eval
进入最后一个
die

读取器
在与参数一起使用时分叉,以打开进程。当父进程返回时,该进程在子进程中执行,并带有其pid。此代码在中

当此操作失败时,子
croak
s会显示您收到的消息。但是父级返回,因为它与子级没有IPC,而子级将通过
exec
消失。因此,父对象逃逸并沿着
eval
向下移动。该进程没有
$@
,如果($@),则绕过

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

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

# ...
$pipe->close;
exit if $? != 0;
(或者更确切地说,先检查一下)。这仍然是一个“修复”,可能并不总是有效。其他探测
$pipe
或查找逃犯PID的方法有点模糊(或者更糟的是,深入挖掘类内部)

另一方面,从程序中收集输出和退出代码的简单方法是使用模块。一个好的选择是。还有其他的,像和,或核心,但相当低的水平


经过澄清,正常的
打开
也应该足够了。

作为旁白,我使用IO::Pipe是因为我想收集正在运行的程序的输出及其退出代码,如果命令无法运行,我想生成一个异常;如果有更好的方法,尤其是不存在上述问题的方法,请在这里的评论中告诉我。从简单到强大,有很好的模块用于收集输出和退出代码。一旦你有了退出密码,你就可以询问它并做你想做的事,当然你可以扔一个
die
。如果我正确理解了目的——运行一个程序并获取其输出,然后选中exit——我不明白为什么要使用
IO::Pipe
。如果只需要读取或写入外部进程,而不需要同时读取或写入外部进程,则使用;或或(比IPC::Open3更好的接口,但不在core中),如果您需要同时读取和写入进程。如果您只是从管道中读取,则IPC::Open3确实可以,并且它会根据需要将启动子程序的错误传播到父程序中的异常中。(我自己将这一功能添加到open3中。)
$ perl example.pl pipe foo
Caught Exception: IO::Pipe: Cannot exec: No such file or directory at example.pl line 15.

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