如何在不使用 eval 的情况下动态包含 Perl 模块?

发布于 2024-08-14 13:00:38 字数 277 浏览 4 评论 0原文

我需要动态包含 Perl 模块,但如果可能的话,由于工作编码标准,我希望远离 eval。这是可行的:

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

但如果可能的话,我需要一种无需 eval 即可完成此操作的方法。所有 google 搜索都会导致 eval 方法,但不会以任何其他方式进行。

是否可以在没有eval的情况下做到这一点?

I need to dynamically include a Perl module, but if possible would like to stay away from eval due to work coding standards. This works:

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

But I need a way to do it without eval if possible. All google searches lead to the eval method, but none in any other way.

Is it possible to do it without eval?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

埖埖迣鎅 2024-08-21 13:00:38

使用 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 {...} 或 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 = $@;
    # ...
};

Use require to load modules at runtime. It often a good idea to wrap this in a block (not string) eval in case the module can't be loaded.

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
};

The reason for the eval {...} or do {...} syntax and making a copy of $@ is because $@ is a global variable that can be set by many different things. You want to grab the value as atomically as possible to avoid a race condition where something else has set it to a different value.

If you don't know the name of the module until runtime you'll have to do the translation between module name (My::Module) and file name (My/Module.pm) manually:

my $module = 'My::Module';

eval {
    (my $file = $module) =~ s|::|/|g;
    require $file . '.pm';
    $module->import();
    1;
} or do {
    my $error = $@;
    # ...
};
随梦而飞# 2024-08-21 13:00:38

如何使用核心模块 Module::Load

以您的示例:

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

“Module::Load - 两者的运行时要求模块和文件”

“加载使您无需知道您是否需要文件或模块。”

如果失败,它将因类似“无法在 @INC 中定位 xxx(@INC 包含:...”)而终止。

How about using the core module Module::Load

With your example:

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

"Module::Load - runtime require of both modules and files"

"load eliminates the need to know whether you are trying to require either a file or a module."

If it fails it will die with something of the like "Can't locate xxx in @INC (@INC contains: ...".

鱼窥荷 2024-08-21 13:00:38

好吧,总是有 require

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

注意,你会失去任何东西您可能会从编译时而不是运行时调用 import 中获得效果。

编辑:此方法和 eval 方法之间的权衡是:eval 允许您使用正常的模块语法,并在模块名称无效(而不是仅仅找不到)时给出更明确的错误。 OTOH,eval 方式(可能)更容易受到任意代码注入的影响。

Well, there's always require as in

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

Note that you lose whatever effects you may have gotten from the import being called at compile time instead of runtime.

Edit: The tradeoffs between this and the eval way are: eval lets you use the normal module syntax and gives you a more explicit error if the module name is invalid (as opposed to merely not found). OTOH, the eval way is (potentially) more subject to arbitrary code injection.

油饼 2024-08-21 13:00:38

不,没有eval是不可能的,因为require()需要裸字模块名称,如perldoc -f require。然而,这并不是对 eval 的恶意使用,因为它不允许注入任意代码(当然,假设您可以控制您需要的文件的内容)。

编辑: 代码已修改如下,但为了完整性,我保留第一个版本。

我使用 我曾经使用这个小糖模块在运行时进行动态加载:

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;

No, it's not possible to without eval, as require() needs the bareword module name, as described at perldoc -f require. However, it's not an evil use of eval, as it doesn't allow injection of arbitrary code (assuming you have control over the contents of the file you are requireing, of course).

EDIT: Code amended below, but I'm leaving the first version up for completeness.

I use I used to use this little sugar module to do dynamic loads at runtime:

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. I'm staring at this definition (I wrote it quite a while ago) and I'm pondering adding this:
$class->export_to_level(1, undef, @imports);... it should work, but is not tested.

EDIT: version 2 now, much nicer without an eval (thanks 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;
怪我闹别瞎闹 2024-08-21 13:00:38

CPAN 上的 Class::MOP 有一个 load_class 方法:
http://metacpan.org/pod/Class::MOP

Class::MOP on CPAN has a load_class method for this:
http://metacpan.org/pod/Class::MOP

若有似无的小暗淡 2024-08-21 13:00:38

我喜欢做这样的事情:

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

i like doing things like..

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文