Как я могу обойти вызов 'die' в библиотеке Perl, которую я не могу изменить?
-
19-08-2019 - |
Вопрос
Да, проблема связана с библиотекой, которую я использую, и нет, я не могу ее изменить.Мне нужен обходной путь.
По сути, я имею дело с плохо написанной библиотекой Perl, которая завершается с "die", когда при чтении файла возникает определенная ошибка.Я вызываю эту процедуру из программы, которая перебирает тысячи файлов, горстка из которых плохая.Случаются плохие файлы;Я просто хочу, чтобы моя процедура регистрировала ошибку и двигалась дальше.
ЕСЛИ БЫ я МОГ изменить библиотеку, я бы просто изменил
die "error";
к a
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().Каков наиболее правильный / элегантный способ разработки системы обнаружения ошибок в подпрограмме и вызывающей подпрограмме, чтобы я получал правильное поведение как для перехваченных, так и для неперехваченных ошибок?
Решение
Вы можете обернуть его в 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 @_; } }
Если нет, то может ловушка IT.(поищите $ 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
.
Какая-то комбинация этого будет работать.
Хотя изменение 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 , где я показываю некоторые другие приемы Что-то в этом роде. Хитрость в том, чтобы не сломать вещи еще больше. То, как вы не сломаете вещи, зависит от того, что вы делаете.