2010-03-04 79 views
9

我發現在我的Perl腳本中重複出現以下反模式:腳本包含一些機器/設置特定設置,我將它們作爲常量存儲在腳本中,而其餘部分腳本是一般性質:在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

我的最愛是Config::Std。我喜歡它處理multi-linemulti-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' 
     ];
+0

+1非常有幫助 - 謝謝。好像應該有一個模塊可以爲我們生成'template_dirs'這樣的方法。我偶然發現了'Config :: General',特別是它的'-ExtendedAccess'選項。從未使用過它,但它看起來很有趣。 – FMc

+0

@FM我不知道「Config :: General」。它確實看起來很有趣,但我發現有時一般的解決方案可能有點過於籠統。當然,可以自動爲標量值選項自動生成訪問器,並分別爲潛在的多值選項自動生成訪問器。 –

1

通常的低技術方法是簡單地將do EXPR作爲配置文件。你看過這個嗎?

+5

這提供了沒有錯誤檢查任何,並且允許任意代碼執行。不要評估表達式或文件的內容,而只需將它們的內容讀入變量。 – Ether

+1

@其他'do'方法是內置的,簡單且常用的。許多大型或流行的軟件包仍然使用它來支持更安全但更復雜的配置方案,例如http://gna.org/projects/savane/和http://packages.debian.org/sbuild和Net :: Config(包含在Perl發行版中)。只要您相信創建配置文件的用戶,這就完全沒問題。 – ephemient

2

對於配置數據,我更喜歡YAMLYAML::XS。它非常簡單,易讀,並且幾乎可以綁定任何編程語言。另一種流行的選擇是Config::General

7

對於配置文件,我喜歡使用YAML。簡單,跨平臺,人性化的讀取,而且不會出現您的配置意外變形爲實際程序的危險。

+3

我喜歡YAML :: Tiny,因爲它是輕量級和純Perl的(如果需要可以很容易地進行捆綁)。 –

+0

它是否是人類可寫的? – mob

+2

@mobrule:我會稱之爲人爲編輯。改變現有值是微不足道的。從頭開始手動創建YAML文件有點困難。 –

1

在被笑出來類的危險,一種解決方案是存儲在配置在XML(或者更具冒險精神的JSON)。 Perl以外的可互操作的人類消費品不必駐留在本地PC上(可以從「配置URL」中請求XML和JSON)和一堆標準模塊(XML :: Simple通常足夠好用於配置XML文件)存在於CPAN上。

0

不要將自己綁定到格式 - 使用Config::Any,或多一點whizbang DWIM因子,Config::JFDI(它本身包裝Config :: Any)。有了它們,您就可以購買支持INI,YAML,XML,Apache-style config等的能力。

Config :: JFDI通過嘗試捕獲Catalyst的配置加載器的一些魔力而構建它:將實例本地配置與應用程序範圍配置,環境變量支持和有限的宏設施合併(__path_to(foo/bar)__派上用場出奇的人往往)

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。