Подтвердить что ты не робот

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

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

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

ЕСЛИ Я МОГУ ИЗМЕНИТЬ библиотеку, я просто изменил бы

die "error";

до

print "error";return;

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

ВОПРОС ПОСЛЕДУЮЩИХ ВОПРОСОВ: использование "eval" для кушетки, вызванный сбоем, работает хорошо, но как мне настроить обработку ошибок, которые могут быть обнаружены в этой структуре? Чтобы описать:

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

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

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

4b9b3361

Ответ 1

Вы можете обернуть его в eval. См:

perldoc -f eval

Например, вы можете написать:

# warn if routine calls die
eval { routine_might_die }; warn [email protected] if [email protected];

Это превратит фатальную ошибку в предупреждение, которое более или менее то, что вы предложили. Если вызывается die, [email protected] содержит строку, переданную ему.

Ответ 2

Разве это ловушка $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 a good death.";
            die @_;
       }
    } 
    
  • Если нет, trap it. (ищите $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 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.

Некоторая комбинация этого будет работать.

Ответ 3

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