34

はい、問題は私が使用しているライブラリにあります。いいえ、変更できません。回避策が必要です。

基本的に、私は、ファイルの読み取り中に特定のエラー状態が発生したときに「die」で終了する、不適切に作成された Perl ライブラリを扱っています。私はこのルーチンを何千ものファイルをループしているプログラムから呼び出していますが、そのうちのいくつかは悪いものです。不良ファイルが発生します。ルーチンでエラーをログに記録して先に進みたいだけです。

ライブラリを変更できる場合は、単に変更します

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__}ますか?もしそうなら、それはあなたよりもローカルです。ただし、いくつかの戦略があります。

  • そのパッケージを呼び出してダイをオーバーライドできます。

    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 を探します。マークダウンは完全なリンクを処理していません。)

    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

dieaを 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 に答える