9

我发现在我的 Perl 脚本中重复了以下反模式:脚本包含一些特定于机器/设置的设置,我将这些设置作为常量内联存储在脚本中,而脚本的其余部分本质上是通用的:

#!/usr/bin/perl

use strict;
use warnings;

# machine specific settings at the start of the script.
my $SETTING_1 = "foo";
my @SETTING_2 = ("123", "456");
my $SETTING_3 = "something";

# general part of script follows.
...

这种模式在一台机器上运行时有点好,但是一旦我想将脚本分发到多台机器,麻烦就开始了,因为我必须跟踪,这样我就不会在常规部分用新的更新覆盖设置部分。

正确的解决方案显然是拥有一个通用脚本文件并让它读取一个特定于脚本运行环境的配置文件。

我的问题是:你会推荐什么 CPAN 模块来解决这个问题?为什么?

4

8 回答 8

7

对于配置文件,我喜欢使用YAML。简单、跨平台、人类可读,并且不会有您的配置意外变成实际程序的危险。

于 2010-03-04T17:59:44.933 回答
4

我最喜欢的是Config::Std。我喜欢它处理多行多部分配置值的方式。

当变量可能是多值时你必须小心:如果配置文件中存在单个值,它将将该值存储在标量中;如果存在多个值,您将获得一个数组引用。

我发现有两个配置文件很方便:一个用于描述操作环境的值(在哪里可以找到库等),另一个用于用户可修改的行为。

我也喜欢围绕它写一个包装器。例如(更新为包括自动生成的只读访问器):

#!/usr/bin/perl

package My::Config;
use strict; use warnings;

use Config::Std;
use FindBin qw($Bin);
use File::Spec::Functions qw( catfile );

sub new {
    my $class = shift;
    my ($config_file) = @_;

    $config_file = catfile($Bin, 'config.ini');
    read_config $config_file => my %config;

    my $object = bless \%config => $class;

    $object->gen_accessors(
        single => {
            install => [ qw( root ) ],
        },
        multi => {
            template => [ qw( dir ) ],
        },
    );

    return $object;
}

sub gen_accessors {
    my $config = shift;
    my %args = @_;

    my $class = ref $config;

    {
        no strict 'refs';
        for my $section ( keys %{ $args{single} } ) {
            my @vars = @{ $args{single}->{$section} };
            for my $var ( @vars ) {
                *{ "${class}::${section}_${var}" } = sub {
                    $config->{$section}{$var};
                };
            }
        }

        for my $section ( keys %{ $args{multi} } ) {
            my @vars = @{ $args{multi}->{$section} };
            for my $var ( @vars ) {
                *{ "${class}::${section}_${var}" } = sub {
                    my $val = $config->{$section}{$var};
                    return [ $val ] unless 'ARRAY' eq ref $val;
                    return $val;
                }
            }
        }
    }

    return;
}

package main;

use strict; use warnings;

my $config = My::Config->new;

use Data::Dumper;
print Dumper($config->install_root, $config->template_dir);
C:\Temp> 猫 config.ini
[安装]
根 = c:\opt

[模板]
目录 = C:\opt\app\tmpl
目录 = C:\opt\common\tmpl

输出:

C:\Temp> g.pl
$VAR1 = 'c:\\opt';
$VAR2 = [
          'C:\\opt\\app\\tmpl',
          'C:\\opt\\common\\tmpl'
        ];
于 2010-03-04T18:33:12.620 回答
2

对于配置数据,我更喜欢YAMLYAML::XS 。它简单、易读,并且具有几乎所有编程语言的绑定。另一个流行的选择是Config::General

于 2010-03-04T17:58:41.597 回答
2

Config:Properties库非常适合读取和写入键/值对属性文件。

于 2010-03-04T18:05:51.517 回答
1

通常的低技术方法是简单地EXPR一个配置文件。你调查过这个吗?

于 2010-03-04T17:55:31.357 回答
1

冒着被笑出课堂的风险,一种解决方案是将配置存储在 XML(或者更冒险的 JSON)中。Perl 之外的人工消费、互操作性,不必存在于本地 PC(XML 和 JSON 都可以从“配置 URL”请求)和一堆标准模块(XML::Simple 通常足以满足config XML 文件)存在于 CPAN 上。

于 2010-03-05T00:14:55.497 回答
1

对于像这样的简单配置,特别是对于我不希望这些数据在现实世界中改变的琐碎事情,我经常简单地使用 YAML。简单是无法击败的:

首先,编写包含您的配置的 Perl 数据结构。

use YAML;

my $SETTINGS = {
    '1' => "foo",
    '2' => ["123", "456"],
    '3' => "something",
};

然后,将其传递给 YAML::DumpFile();

YAML::DumpFile("~/.$appname.yaml", $SETTINGS);

删除数据结构并替换为

my $SETTINGS = YAML::LoadFile("~/.$appname.yaml");

然后忘记它。即使您不知道或想学习 YAML 语法,也可以手动对配置进行小的更改,并且可以在 Perl 中完成更多主要更改,然后重新转储到 YAML。

于 2010-03-05T13:30:07.600 回答
0

不要将自己束缚在一种格式上——使用Config::Any,或者更多的 whizbang DWIM 因素Config::JFDI(它本身包装了 Config::Any)。有了它们,您就可以为自己购买支持 INI、YAML、XML、Apache 风格配置等的能力。

Config::JFDI 在此基础上尝试捕捉 Catalyst 配置加载器的一些魔力:将实例本地配置与应用程序范围的配置、环境变量支持和有限的宏工具合并(__path_to(foo/bar)__令人惊讶地经常派上用场。)

于 2010-03-05T13:11:38.477 回答