変更できないPerlライブラリで「die」呼び出しを回避するにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/451227

  •  19-08-2019
  •  | 
  •  

質問

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

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

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

die "error";

print "error";return;

が、できません。不良ファイルがプロセス全体をクラッシュさせないように、ルーチンを無効にする方法はありますか?

フォローアップの質問:<!> quot; eval <!> quot;クラッシュを起こしやすい呼び出しをうまく処理するために、そのフレームワーク内でキャッチ可能なエラーの処理を設定するにはどうすればよいですか?説明するには:

時々クラッシュするライブラリを何度も呼び出すサブルーチンがあります。このサブルーチン内の各呼び出しをeval {}で無効にするのではなく、単にそれを停止させ、サブルーチンを呼び出すレベルでeval {}を使用します。

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

ただし、function()でキャッチできるエラー条件があります。キャッチされたエラーとキャッチされていないエラーの両方に対して正しい動作が得られるように、サブルーチンおよび呼び出しルーチンでエラーキャッチを設計する最も適切/エレガントな方法は何ですか?

役に立ちましたか?

解決

evalで囲むことができます。参照:

perldoc -f eval

たとえば、次のように書くことができます:

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

これにより、致命的なエラーが警告に変わります。 dieが呼び出された場合、$@には渡された文字列が含まれます。

他のヒント

それは$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としてアドレス指定することです。

これのいくつかの組み合わせは機能します。

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

誰かが元のモジュールを更新しても、あなたのコピーは残ります(ただし、変更をマージする必要があるかもしれません)。

これについては、 Perlをマスターするでかなり説明しています。そのようなこと。秘Theは、物事をさらに壊さないことです。物事を壊さないようにする方法は、何をしているのかによって異なります。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top