include / eval perl文件到运行时定义的唯一命名空间

时间:2017-08-24 21:00:03

标签: perl

我正在编写一个必须导入许多其他perl配置文件的工具。这些文件没有包装,可能有相似或相互矛盾的变量/功能。我没有能力改变这些文件的格式,所以我必须解决它们的问题。我想要做的是将每个导入到一个唯一的名称空间,但我找不到使用dorequireuse的方法。如果我不使用动态名称,只使用硬编码名称,我就可以做到。

想要这样的事情:

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似乎没有能力从字符串构建变量名称并将其用作变量。

2 个答案:

答案 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的userequire工具使用您在@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

显然,您可以定义自己的映射,从组成模块名称到文件名。