2009-12-16 3 views
24

Мне нужно динамически включать модуль Perl, но, если возможно, хотелось бы держаться подальше от eval из-за стандартов рабочего кодирования. Это работает:Как я могу динамически включать Perl-модули без использования eval?

$module = "My::module"; 
eval("use $module;"); 

Но мне нужен способ сделать это без eval, если это возможно. Все поисковые запросы Google приводят к методу eval, но ни в каком другом виде.

Можно ли это сделать без eval?

+1

Duplicate: http://stackoverflow.com/questions/442710/how-do-i-use-a-perl-package-known-only-in-runtime –

+0

Извините, что я искал, но не смог, t найти что-нибудь. – user226723

+2

Я бы обновил ваши стандарты кодирования. Нет ничего неправильного или небезопасного в том, строка. Это самый простой способ выполнить то, что вы хотите. Если вы передаете вход пользователя, это еще одна история ... – runrig

ответ

43

Используйте require для загрузки модулей во время выполнения. Часто рекомендуется обернуть это в блок (не строку) eval, если модуль не может быть загружен.

eval { 
    require My::Module; 
    My::Module->import(); 
    1; 
} or do { 
    my $error = [email protected]; 
    # Module load failed. You could recover, try loading 
    # an alternate module, die with $error... 
    # whatever's appropriate 
}; 

Причина eval {...} or do {...} синтаксиса и сделать копию [email protected] потому, что [email protected] глобальная переменная, которая может быть установлена ​​на много разных вещей. Вы хотите получить значение как можно более атомарно, чтобы избежать состояния гонки, когда что-то еще установило его на другое значение.

Если вы не знаете имя модуля до выполнения вы должны будете сделать перевод между именем модуля (My :: Module) и имя файла (My/Module.pm) вручную:

my $module = 'My::Module'; 

eval { 
    (my $file = $module) =~ s|::|/|g; 
    require $file . '.pm'; 
    $module->import(); 
    1; 
} or do { 
    my $error = [email protected]; 
    # ... 
}; 
+0

Хм. Это хорошо, потому что вы завершаете требование в eval, но ваш запрос не является особенно динамичным. Если бы можно было объединить ответы, то Дэн и твои были бы моими лучшими кандидатами. – innaM

+0

Хотя его требование не было динамичным, я легко мог это сделать (как сказал бы Пикард). Спасибо за ответ! – user226723

+0

Это не очень поточно, например. см. http://www.perlfoundation.org/perl5/index.cgi?exception_handling – Ether

10

Ну, всегда есть require как в

require 'My/Module.pm'; 
My::Module->import(); 

Обратите внимание, что вы потеряете все, что эффекты, которые, возможно, получили от import вызывается во время компиляции вместо выполнения.

Редактировать: Компромиссы между этим и eval способом: eval позволяет использовать стандартный синтаксис модуля и дает более явную ошибку, если имя модуля недействительно (в отличие от просто не найденного). OTOH, метод eval (потенциально) более подвержен произвольной инъекции кода.

+0

Спасибо за ответ. Я проголосовал за вас, так как не могу выбрать оба ответа :). – user226723

4

Нет, это невозможно без eval, так как require() нуждается в имени имени голого слова, как описано в perldoc -f require. Тем не менее, это не злое использование eval, поскольку оно не позволяет вводить произвольный код (при условии, что у вас есть контроль над содержимым файла require ing, конечно).

EDIT: Код изменен ниже, но я оставляю первую версию для полноты.

Я использую я имел обыкновение использовать этот маленький сахар модуль делать динамические нагрузки во время выполнения:

package MyApp::Util::RequireClass; 

use strict; 
use warnings; 

use Exporter 'import'; # gives you Exporter's import() method directly 
our @EXPORT_OK = qw(requireClass); 

# Usage: requireClass(moduleName); 
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports); 
sub requireClass 
{ 
    my ($class) = @_; 
    eval "require $class" or do { die "Ack, can't load $class: [email protected]" }; 
} 

1; 

PS. Я смотрю на это определение (я написал его довольно давно), и я размышляю над тем, чтобы добавить это: $class->export_to_level(1, undef, @imports); ... его должен работать, но не проверен.

EDIT: версия 2 теперь гораздо лучше без Eval (спасибо ysth): :)

package MyApp::Util::RequireClass; 

use strict; 
use warnings; 

use Exporter 'import'; # gives you Exporter's import() method directly 
our @EXPORT_OK = qw(requireClass); 

# Usage: requireClass(moduleName); 
# does not do imports (wrong scope) -- you should do this after calling me: $class->import(@imports); 
sub requireClass 
{ 
    my ($class) = @_; 

    (my $file = $class) =~ s|::|/|g; 
    $file .= '.pm'; 
    require $file; # will die if there was an error 
} 

1; 
+3

«require() требует имя модуля с открытым словом» - нет, это не так; просто s # :: #/# g; и добавьте '.pm', чтобы получить имя пути. – ysth

+0

@ysth: ничего себе, вы правы ... документы очень расплывчаты в этом, но он действительно работает. Ну, это великолепно, теперь я могу пересмотреть свой модуль, чтобы устранить этот злой eval :) – Ether

16

Как об использовании модуля Module::Load

ядро ​​с вашим примером:

use Module::Load; 
my $module = "My::module"; 
load $module; 

«Module :: Load - среда требует от обоих модулей и файлов»

«нагрузки устраняет необходимость знать ли вы пытаетесь требовать файл или модуль.»

Если он не будет умирать с чем-то тому подобное «Не удается найти ххх в @INC (@INC содержит: ...».

+1

Это основной модуль, поэтому его следует использовать. – alexk

0

я люблю делать такие вещи, как ..

require Win32::Console::ANSI if ($^O eq "MSWin32");