如何绕过我无法修改的Perl库中的'die'调用?

时间:2009-01-16 17:11:34

标签: perl error-handling die

是的,问题在于我正在使用的库,不,我无法修改它。我需要一个解决方法。

基本上,我正在处理一个写得很糟糕的Perl库,当遇到读取文件的某个错误条件时,它会以'die'退出。我从一个程序中调用这个例程,该程序循环遍历数千个文件,其中一些文件很糟糕。坏文件发生;我只想让我的例程记录错误并继续前进。

如果我可以修改库,我只需更改

die "error";

print "error";return;

,但我不能。有什么方法可以解决这个例程,以便坏文件不会崩溃整个过程吗?

关注问题:使用“eval”来解决容易崩溃的调用很有效,但如何在该框架内设置可捕获错误的处理?描述:

我有一个子程序调用库 - 崩溃 - 有时多次。而不是使用eval {}在这个子例程中调用每个调用,我只是让它死掉,并在调用我的子例程的级别上使用eval {}:

my $status=eval{function($param);};
unless($status){print $@; next;}; # print error and go to next file if function() fails

但是,我可以在函数()中捕获错误条件。在子例程和调用例程中设计错误捕获的最恰当/优雅的方法是什么,以便我捕获和捕获的错误都得到正确的行为?

3 个答案:

答案 0 :(得分:65)

您可以将其包装在eval中。参见:

perldoc -f eval

例如,你可以写:

# warn if routine calls die
eval { routine_might_die }; warn $@ if $@;

这会将致命错误变成警告,这或多或少都是您所建议的。如果调用die,则$@包含传递给它的字符串。

答案 1 :(得分:27)

是否陷阱$SIG{__DIE__}?如果是这样,那么它比你更本地化。但是有几个策略:

  • 你可以唤起它的包并override死:

    package Library::Dumb::Dyer;
    use subs 'die';
    sub die {
        my ( $package, $file, $line ) = caller();
        unless ( $decider->decide( $file, $package, $line ) eq 'DUMB' ) {
            say "It's a good death.";
            die @_;
       }
    } 
    
  • 如果没有,可以trap。 (在页面上查找$ SIG,markdown不处理完整链接。)

    my $old_die_handler = $SIG{__DIE__};
    sub _death_handler { 
        my ( $package, $file, $line ) = caller();
        unless ( $decider->decide( $file, $package, $line ) eq 'DUMB DIE' ) {
            say "It's a good death.";
            goto &$old_die_handler;
        }
    }
    $SIG{__DIE__} = \&_death_handler;
    
  • 您可能需要扫描库,找到始终调用的子类,并使用它来覆盖$SIG来加载that处理程序。< / p>

    my $dumb_package_do_something_dumb = \&Dumb::do_something_dumb;
    *Dumb::do_something_dumb = sub { 
        $SIG{__DIE__} = ...
        goto &$dumb_package_do_something_dumb;
    };
    
  • 或覆盖内置始终调用...

    package Dumb; 
    use subs 'chdir';
    sub chdir { 
        $SIG{__DIE__} = ...
        CORE::chdir @_;
    };
    
  • 如果一切都失败了,你可以用这个鞭打马的眼睛:

    package CORE::GLOBAL;
    use subs 'die';
    
    sub die { 
        ... 
        CORE::die @_;
    }
    

这将全局覆盖 die ,唯一可以返回die的方法是将其作为CORE::die来解决。

这种方法的某种组合可行。

答案 2 :(得分:8)

虽然将die更改为not die具有其他答案中显示的特定解决方案,但通常您可以始终覆盖其他包中的子例程。您根本不会更改原始来源。

首先,加载原始包,以便获得所有原始定义。原件到位后,您可以重新定义麻烦的子程序:

 BEGIN {
      use Original::Lib;

      no warnings 'redefine';

      sub Original::Lib::some_sub { ... }
      }

您甚至可以剪切和粘贴原始定义并调整所需内容。这不是一个很好的解决方案,但是如果你不能改变原始来源(或者想要在更改原始内容之前尝试一些东西),它就可以工作。

除此之外,您还可以将原始源文件复制到应用程序的单独目录中。由于您控制该目录,因此可以编辑其中的文件。您可以通过将该目录添加到Perl的模块搜索路径来修改该副本并加载它:

use lib qw(/that/new/directory);
use Original::Lib;  # should find the one in /that/new/directory

即使有人更新原始模块,您的副本仍然存在(尽管您可能需要合并更改)。

我在Mastering Perl中讨论了这个问题,在那里我展示了其他一些技巧来做这类事情。诀窍是不要破坏更多的东西。你怎么不打破东西取决于你在做什么。