我发现以下反模式在我的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模块来解决这个问题?为什么?
解决方法
我最喜欢的是
Config::Std.我喜欢它处理
multi-line和
multi-part配置值的方式.
变量可能是多值时必须小心:如果配置文件中存在单个值,它将以标量存储该值;如果存在多个值,您将获得一个数组引用.
我发现有两个配置文件是方便的:一个用于描述操作环境的值(在哪里可以找到库等),另一个用于用户可修改的行为.
我也想写一个包装纸.例如(更新为包括自动生成的只读访问器):
#!/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> cat config.ini [install] root = c:\opt [template] dir = C:\opt\app\tmpl dir = C:\opt\common\tmpl
输出:
C:\Temp> g.pl $VAR1 = 'c:\\opt'; $VAR2 = [ 'C:\\opt\\app\\tmpl','C:\\opt\\common\\tmpl' ];