変更できないPerlライブラリで「die」呼び出しを回避するにはどうすればよいですか?
-
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は、物事をさらに壊さないことです。物事を壊さないようにする方法は、何をしているのかによって異なります。