我正在编写一个必须导入许多其他perl配置文件的工具。这些文件没有包装,可能有相似或相互矛盾的变量/功能。我没有能力改变这些文件的格式,所以我必须解决它们的问题。我想要做的是将每个导入到一个唯一的名称空间,但我找不到使用do
,require
或use
的方法。如果我不使用动态名称,只使用硬编码名称,我就可以做到。
想要这样的事情:
sub sourceTheFile {
my ($namespace, $file) = @_;
package $namespace;
do $file;
1;
return;
}
这不起作用,因为package命令需要名称的常量。那么我尝试这样的事情:
sub sourceTheFile {
my ($namespace, $file) = @_;
eval "package $namespace;do $file;1;"
return;
}
但是do
读取的文件内容放在main::
范围内而不是我想要的范围内。目标范围已创建,但未填充
do
。 (我试过要求,并且在eval中只有一个直的cat $file
。)
我正在使用Devel::Symdump
来验证命名空间是否正确构建。
示例输入文件:
my $xyz = "some var";
%all_have_this = ( common=>"stuff" );
其他挑战
使用临时文件构建的答案并进行调用,我可以根据需要动态地进行此操作。但是,很大但是,我现在如何引用这个新命名空间中的数据? Perl似乎没有能力从字符串构建变量名称并将其用作变量。
答案 0 :(得分:3)
我不确定为什么eval
不起作用。也许是个bug?这是使用临时文件的变通方法。这对我有用:
use strict;
use warnings;
use Devel::Symdump;
use File::Temp;
my $file = './test.pl';
my $namespace = 'TEST';
{
my $fh = File::Temp->new();
print $fh "package $namespace;\n";
print $fh "do '$file';\n";
print $fh "1;\n";
close $fh;
do $fh->filename;
}
答案 1 :(得分:3)
Perl的use
和require
工具使用您在@INC
中安装的任何挂钩。您可以简单地安装一个查找特定位置的挂钩,以使用您选择的前缀加载模块:
package MyIncHook;
use strict;
use warnings;
use autouse Carp => qw( croak );
use File::Spec::Functions qw( catfile );
sub import {
my ($class, $prefix, $location) = @_;
unshift @INC, _loader_for($prefix, $location);
return;
}
sub _loader_for {
my $prefix = shift;
my $location = shift;
$prefix =~ s{::}{/}g;
return sub {
my $self = shift;
my $wanted = shift;
return unless $wanted =~ /^\Q$prefix/;
my $path = catfile($location, $wanted);
my ($is_done);
open my $fh, '<', $path
or croak "Failed to open '$path' for reading: $!";
my $loader = sub {
if ($is_done) {
close $fh
or croak "Failed to close '$path': $!";
return 0;
}
if (defined (my $line = <$fh>)) {
$_ = $line;
return 1;
}
else {
$_ = "1\n";
$is_done = 1;
return 1;
}
};
(my $package = $wanted) =~ s{/}{::}g;
$package =~ s/[.]pm\z//;
my @ret = (\"package $package;", $loader);
return @ret;
}
}
__PACKAGE__;
__END__
显然,请根据您的要求修改$path
的构造。
你可以像这样使用它:
#!/usr/bin/env perl
use strict;
use warnings;
use MyIncHook ('My::Namespace', "$ENV{TEMP}/1");
use My::Namespace::Rand;
print $My::Namespace::Rand::settings{WARNING_LEVEL}, "\n";
$ENV{TEMP}/1/My/Namespace/Rand.pm
包含:
%settings = (
WARNING_LEVEL => 'critical',
);
输出:
C:\Temp> perl t.pl
critical
显然,您可以定义自己的映射,从组成模块名称到文件名。