34

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

基本上,我正在处理一个写得很糟糕的 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

但是,我可以并且确实在 function() 中捕获了一些错误情况。在子例程和调用例程中设计错误捕获的最合适/优雅的方法是什么,以便我获得捕获和未捕获错误的正确行为?

4

3 回答 3

69

你可以把它包在一个eval. 看:

perldoc -f eval

例如,你可以写:

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

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

于 2009-01-16T17:13:57.887 回答
28

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

  • 您可以调用它的包并覆盖die:

    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 @_;
       }
    } 
    
  • 如果没有,可以捕获它。(在页面上查找 $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

    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.

这些的一些组合将起作用。

于 2009-01-16T18:27:05.400 回答
8

尽管将 a 更改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中对此进行了相当多的讨论,在那里我展示了一些其他技术来做这类事情。诀窍是不要破坏更多的东西。你如何不破坏东西取决于你在做什么。

于 2010-01-27T08:46:49.703 回答