Perl:动态加载模块并访问导出的内容
在学习了 Perl require docs 以及 Stackoverflow 我仍然不知道,一定是缺少一个相当简单的技巧。我在运行时加载一个模块并调用其中的子例程。问题是我不一定知道导出的子例程的名称,但一定有一个并且它被导出。
这些模块看起来都是这样的,即。他们大致遵循从 perlmonks.org 获取的模板
package modules::Test;
use strict;
use Exporter;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw(Exporter);
@EXPORT = (*TestSubSomeUnknownName);
@EXPORT_OK = qw(&TestSubSomeUnknownName);
%EXPORT_TAGS = ( ALL => [qw(&TestSubSomeUnknownName)]
);
sub TestSubSomeUnknownName
{
# return a hash reference
}
然后我可以像这样访问 sub假设我知道它的名字:
use Module::Load;
my $package = "modules::Test";
my $subr = "TestSubSomeUnknownName";
load $package;
# Call the subroutine
my $hashref = $package->$subr;
但是如果有人拼错了包中的名字或者我不知道怎么办?解决方案似乎是使用 EXPORT 之一来查看其中的内容,但如何才能完成呢?
After studying the Perl require docs and other links like this on Stackoverflow I'm still none the wiser, must be missing a fairly simple trick. I'm loading a module at runtime and calling a subroutine in it. The problem is I don't necessarily know the name of the exported subroutine, but there must be one and it is exported.
The modules all look like this, ie. they roughly follow a template taken from perlmonks.org
package modules::Test;
use strict;
use Exporter;
use vars qw($VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS);
$VERSION = 1.00;
@ISA = qw(Exporter);
@EXPORT = (*TestSubSomeUnknownName);
@EXPORT_OK = qw(&TestSubSomeUnknownName);
%EXPORT_TAGS = ( ALL => [qw(&TestSubSomeUnknownName)]
);
sub TestSubSomeUnknownName
{
# return a hash reference
}
Then I can access the sub like this, assuming I know its name:
use Module::Load;
my $package = "modules::Test";
my $subr = "TestSubSomeUnknownName";
load $package;
# Call the subroutine
my $hashref = $package->$subr;
But what if someone mis-spelled the name in a package or I don't know it? The solution seems to be to use one of the EXPORTs to see what's in there, but how can it be done?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
@EXPORT
应包含名称而不是符号。1
。如果您想检查包可以做什么,请使用
can
。@EXPORT
should contain names not symbols.1
at the end of a module.If you want to check what a package can do, use
can
.您可以尝试
@modules::Test::EXPORT
包含的内容。另外,尝试运行$modules::Test::EXPORT[0]->()
;You can try what
@modules::Test::EXPORT
contains. Also, try running$modules::Test::EXPORT[0]->()
;您是否:
对于第一个问题,答案通常是不要自动导出任何内容。如果在
@EXPORT_OK
列表下放置子程序名称列表,则可以在主程序中使用以下语法导出这些子程序:如果要自动导入子程序,可以将其放在<代码>@EXPORT 列表。这些必须以
File::Copy
自动导入复制子例程的方式自动导入。现在,现代 Perl 标准不赞成导出任何内容,因为它会污染用户的命名空间,而不必通知用户。如果您真的非常想导入某些内容,现在的标准是使用
@EXPORT_OK
,因此用户必须在use
语句中列出他们想要导入的子例程。这至少记录了污染情况。一些现代模块(例如 File::Spec)不导入任何内容。您要么必须在子例程中添加模块名称前缀,要么使用面向对象语法(即使像
File::Spec
一样,它也不是真正面向对象的,因为没有)这给我们带来了另一件事:在模块中使用面向对象的 Perl。然后,您不必担心导出任何内容,因为您在 OOPerl 中不需要。
如果您尝试解决第二个问题并且只是尝试从第三方模块查找子例程的名称,请使用
perldoc
命令并查看文档。您可以查看@EXPORT
和@EXPORT_OK
列表来查看导出的内容,但可能有些内容未导出(例如$File:
变量)可能很重要。File::Find
模块中的 :Find::Name如果您真的想深入了解一下,您可以尝试像这样遍历每个包变量:
这将遍历包,向您显示定义的内容,甚至告诉您它是否是子例程。我得到以下输出:
除了定义的子例程之外,我无法弄清楚如何查看它是什么类型的变量。也许其他人可以帮我弄清楚。我可能需要对 eval 做一些事情。
感谢 Eric Storm 的修改
下面将显示它的类型。不幸的是,这会说一切都是标量和全局。 Glob 是可以理解的(它是一个 glob)但是一个标量?我想暂时将这些类型排除在 for 循环之外。
Are you:
For the answer to the first question, the answer is usually Don't export anything automatically. If you put a list of subroutine names under
@EXPORT_OK
list, you can export these subroutines using the following syntax in the main program:If you want to automatically import a subroutine, you can put it in the
@EXPORT
list. These will be automatically imported must the wayFile::Copy
automatically imports the copy subroutine.Now, modern Perl standards frown upon exporting anything because it pollutes the user's namespace without necessarily informing the user. If you really, really want to import something, the standard is now to use
@EXPORT_OK
, so the user has to list the subroutines they want to import in theiruse
statement. This at least documents the pollution.Some modern modules like
File::Spec
import nothing. You either have to prefix the subroutine with the name of the module or use object oriented syntax (even if likeFile::Spec
, it's not really object oriented because there are no objects to orient.)Which brings us to another thing: Use Object Oriented Perl in your modules. Then, you don't have to worry about exporting anything because you don't in OOPerl.
If you're trying to do the second question and simply are trying to find the names of the subroutines from a third party module, use the
perldoc
command and see the documentation. You could look at@EXPORT
and the@EXPORT_OK
list to see what is exported, but there might be things that aren't exported (like the$File::Find::Name
variable in theFile::Find
module) that might be important.If you really want to get down and dirty, you can try going through each package variable like this:
This will go through the package, show you what's defined, and even tell you if it's a subroutine or not. I get the following output:
I wasn't able to figure out how to see what type of variable it is other than it's a defined subroutine. Maybe someone else can help me figure it out. I'd probably have to do something with
eval
.Modification w/ Thanks to Eric Storm
The following will show the type it is. Unfortunately, this will say everything is a scalar and a glob. Glob is understandable (it is a glob) but a scalar? I guess just leave these types out of the for loop for now.
所以最后我回到这个问题 - 也许我有点含糊,但我遇到的问题是动态加载模块,并通过仅查看动态加载的模块来找出从这些模块导出的内容。
经过一番黑客攻击后,以下工作达到了目的:
要加载的包的名称只是一个字符串,加载了包,$exports 行(从 Exporter.pm 复制,我发现单步执行 $_->import) 返回导出符号的列表,然后我可以调用第一个符号。
如果有人可以进一步改进这一点,我很感兴趣。
So finally I return to this question - perhaps I was a bit vague but the problem I have is to dynamically load modules, and find out what's exported from these modules by looking at the dynamically loaded module only.
After a bit of hacking the following works for the purpose:
The name of the package to load is just a string, the package is loaded, the $exports line (copied from Exporter.pm that I found single stepping a $_->import) returns the list of exported symbols and I can then call the first one.
If someone can improve this still further I'm interested.