我需要动态地包含一个Perl模块,但是如果可能的话,由于工作编码标准,我们希望远离eval。这有效:
$module = "My::module";
eval("use $module;");
但是如果可能的话,我需要一种方法来做到eval
。所有谷歌搜索都会导致eval
方法,但没有任何其他方式。
是否可以在没有eval
的情况下执行此操作?
答案 0 :(得分:48)
使用require
在运行时加载模块。如果无法加载模块,通常最好将其包装在一个块(而不是字符串)eval
中。
eval {
require My::Module;
My::Module->import();
1;
} or do {
my $error = $@;
# Module load failed. You could recover, try loading
# an alternate module, die with $error...
# whatever's appropriate
};
eval {...} or do {...}
语法和复制$@
的原因是因为$@
是一个全局变量,可以由许多不同的东西设置。您希望尽可能原子地获取值,以避免竞争条件,其他东西将其设置为不同的值。
如果您在运行时之前不知道模块的名称,则必须手动执行模块名称(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 = $@;
# ...
};
答案 1 :(得分:16)
如何使用核心模块Module::Load
用你的例子:
use Module::Load;
my $module = "My::module";
load $module;
“Module :: Load - 运行时需要模块和文件”
“加载无需知道您是要求文件还是模块。”
如果失败,它将会死于类似的东西“无法在@INC中找到xxx(@INC包含:......”。
答案 2 :(得分:10)
嗯,
中始终有require
require 'My/Module.pm';
My::Module->import();
请注意,在编译时调用import
而不是运行时,可能会失去任何效果。
编辑:这与eval方式之间的权衡是:eval允许您使用普通模块语法,如果模块名称无效(而不是仅仅找不到),则会给出更明确的错误。 OTOH,eval方式(可能)更容易受到任意代码注入。
答案 3 :(得分:4)
不,没有 eval
是不可能的,因为require()
需要裸字模块名称,如perldoc -f require所述。但是,它并不是eval的恶意使用,因为它不允许注入任意代码(假设您可以控制文件的内容,当然是require
。)
编辑:下面修改了代码,但我要完成第一个版本。
我使用 我曾经使用这个小糖模块在运行时进行动态加载:
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: $@" };
}
1;
PS。我正盯着这个定义(我很久以前写过)并且我在考虑添加这个:
$class->export_to_level(1, undef, @imports);
... 应工作,但未经过测试。
编辑:版本2现在,没有评估就更好了(感谢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;
答案 4 :(得分:1)
CPAN上的Class :: MOP有一个load_class方法: http://metacpan.org/pod/Class::MOP
答案 5 :(得分:0)
我喜欢做像......这样的事情。
require Win32::Console::ANSI if ( $^O eq "MSWin32" );