Как я могу обойти вызов " die " в библиотеке Perl, которую я не могу изменить?

Да, проблема в библиотеке, которую я использую, и нет, я не могу ее изменить. Мне нужен обходной путь.

В основном, я имею дело с плохо написанной библиотекой Perl, которая выходит с "die", когда возникает определенное условие ошибки при чтении файла. Я вызываю эту процедуру из программы, которая перебирает тысячи файлов, несколько из которых плохие. Плохие файлы случаются; я просто хочу, чтобы моя процедура регистрировала ошибку и двигалась дальше.

если бы я мог изменить библиотеку, я будет просто изменить

die "error";

до

print "error";return;

, но я не могу. Есть ли способ, которым я могу уложить рутину, чтобы плохие файлы не разбили весь процесс?

FOLLOWUP вопрос: использование "eval" для дивана аварийного вызова работает хорошо, но как я могу настроить обработку ошибок, способных поймать в этой структуре? Описать:

у меня есть подпрограмма, которая вызывает библиотеку-которая-падает-иногда много раз. Вместо дивана каждый вызовите эту подпрограмму с eval {}, я просто позволяю ей умереть и использую eval{} на уровне, который вызывает мою подпрограмму:

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

однако есть условия ошибки, которые я могу и делаю catch в function (). Каков наиболее правильный / элегантный способ разработки ошибки в подпрограмме и вызывающей подпрограмме, чтобы я получил правильное поведение как для пойманных, так и для неучтенных ошибок?

3 ответов


вы могли бы обернуть его в 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 на странице, 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 должен обращаться к нему как 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, где я показываю некоторые другие методы для такого рода вещей. Фокус в том, чтобы не ломать вещи еще больше. То, как вы не ломаете вещи, зависит от того, что вы делаете.