Как мне обойти вызов die в библиотеке Perl, которую я не могу изменить?
Да, проблема связана с библиотекой, которую я использую, и нет, я не могу ее изменить. Мне нужен обходной путь.
По сути, я имею дело с плохо написанной библиотекой 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 ответа
Вы могли бы обернуть это в eval
, Увидеть:
perldoc -f eval
Например, вы можете написать:
# warn if routine calls die
eval { routine_might_die }; warn $@ if $@;
Это превратит фатальную ошибку в предупреждение, более или менее то, что вы предложили. Если die
называется, $@
содержит строку, переданную ему.
Это ловушка $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 на странице, уценка не обрабатывает полную ссылку.)
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
это решить как 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, где я показываю некоторые другие методы для такого рода вещей. Хитрость в том, чтобы не сломать вещи еще больше. То, как вы не сломаете вещи, зависит от того, что вы делаете.