首页 > 解决方案 > 如何使用 Perl 作为配置文件的语言?

问题描述

我有一个应用程序,我想允许用户在 Perl 中指定配置文件。

我想到的类似于 PKGBUILD 在 Arch 中的使用方式:它是一个 Bash 脚本,它只是source来自“makepkg”工具的 d。它包含变量和函数定义:

pkgbase=somepkg
pkgname=('somepkg')
pkgver=0.0.1
...
prepare() {
    cd "${srcdir}/${pkgbase}-${pkgver}"
    ...
}

我想在 Perl 中做这样的事情。我想我正在寻找 Perl 版本source,它可以读取文件、评估文件并将所有子例程和变量导入调用者的命名空间。使用 Bash 版本,我可以想象编写一个 PKGBUILD,它获取另一个 PKGBUILD,然后覆盖一个或两个变量;我希望这种“继承”也可以在我的 Perl 配置文件中实现。

Perl 的一个问题do是它似乎将文件的变量和子例程放入单独的名称空间中。另外,我不知道如何覆盖真正的子例程,只有命名的匿名子例程。

这是一个版本,可以用来说明我想要做什么。它不是很优雅,但它演示了重写子例程和变量,以及调用先前定义的子例程:

$ cat test-override
#!/usr/bin/perl

use warnings;
use strict;

my @tables = qw(a b c);

my $print_tables = sub {
   print join(", ", @tables), "\n";
};

eval(`cat "test-conf"`) or die "$@";

&$print_tables();

$ cat test-conf
@tables = qw(d e f);

my $old_print_tables = $print_tables;
$print_tables = sub {
  warn "In test-conf \$print_tables\n";
  &$old_print_tables();
}

$ ./test-override
In test-conf $print_tables
d, e, f

另一种方法是让配置文件返回一个散列,其中数据和子例程作为值。还有使用类和继承的选项。但是,我希望配置文件在语法上尽可能轻量级。

“do”的文档提到了在配置文件中使用 Perl 的可能性,所以我知道这个问题之前已经考虑过。是否有一个规范的例子说明如何以“用户友好”的方式做到这一点?

标签: perlinheritanceoverridingconfiguration-files

解决方案


您对您希望此“配置文件”执行的操作(将 subs 和变量导入调用者的命名空间、覆盖 subs、类、继承...)的描述听起来像是标准的 Perl 模块。所以使用标准的 Perl 模块。

请注意,这种方法有广泛的先例:标准cpan命令行客户端将其配置存储在一个模块中,该模块位于~/.cpan/CPAN/MyConfig.pm*nix 类型系统的默认路径中。当然,cpan'sMyConfig.pm是一个非常简单的示例,它只设置了 hashref $CPAN::Config,但它没有理由不能做任何模块所做的所有其他事情。

但是做起来do很简单。我怀疑你只是想多了:

$ cat test-override 
#!/usr/bin/perl

use warnings;
use strict;

our @tables = qw(a b c);

sub print_tables {
   print join(", ", @tables), "\n";
};

print_tables;

do "test-conf";

print_tables;

print "\@tables is @tables\n";

$ cat test-conf 
@tables = qw(d e f);

sub print_tables {
  print "print_tables from test_conf\n";
}

$ ./test-override 
a, b, c
print_tables from test_conf
@tables is d e f

我所做的重要更改@tables是将其从my仅在当前范围和当前文件中可见的 更改our为在同一个包中的任何位置都可见(或者如果它具有包名称,则从其他包中可见)。

但是print_tables配置文件中的 my 不会调用 original print_tables,而您在那个上不走运。因为只能有一个&main::print_tables,替换它会完全覆盖原来的那个,不再存在。如果您希望能够覆盖它并且仍然能够调用原始声明,则需要将两个声明放入不同的包中,这意味着使用 OO Perl(这样您就可以多态地调用正确的)。

另请注意,use具有与 相同的词法范围,my意味着您use strict; use warnings;不会延续到 conf 文件中。您可以通过在我的版本中添加 来轻松演示这一点,此时它将生成警告。use warnings;test-confSubroutine print_tables redefined


推荐阅读