2009-10-06 179 views
1

我目前正在開發一個監視軟件,它接收服務器名稱和IP地址的輸入文件並創建一個基本的信息數據庫。我想在處理配置文件時默認一些值,並且它在第一次循環時工作正常,但隨後的任何條目都會被奇怪地創建(奇怪的是我最好的方式來描述它,因爲它可能是正確的,代碼是錯誤的,因爲代碼正在做我剛纔要求它做的事情,但不一定是我想要做的事情)。使用散列數組填充散列數組

從下面的代碼輸出的樣子:

$VAR1 = [ 
     { 
     'IPAddress' => '196.8.150.163', 
     'Boxname' => 'MPLRDFDSOAK1', 
     'CurrentStatusInfo' => { 
           'LineHandlersRunning' => [ 
                  { 
                   'NumberOfGaps' => 0, 
                 'LineHandlerName' => 'DEFAULT', 
                 'NumberOfCommLinkDowns' => 0, 
                   'LineHandlerUpTime' => 0, 
                   'MemoryUsage' => 0 
                  } 
                  ] 
           }, 
     'PreviousStatusInfo' => { 
            'LineHandlersRunning' => 
         $VAR1->[0]{'CurrentStatusInfo'}{'LineHandlersRunning'}[0] 
                 ] 
           } 
     }, 
     { 
     'IPAddress' => '196.8.150.164', 
     'Boxname' => 'MPLRDFDSOAK2', 
     'CurrentStatusInfo' => { 
           'LineHandlersRunning' => 
         $VAR1->[0]{'CurrentStatusInfo'}{'LineHandlersRunning'} 
           }, 
     'PreviousStatusInfo' => { 
            'LineHandlersRunning' => 
         $VAR1->[0]{'PreviousStatusInfo'}{'LineHandlersRunning'} 
           } 
     } 
    ]; 

以下是代碼:

####################################################################################### 
# Version History                  # 
####################################################################################### 
# example of the ini file 
#box=>MPLRDFDSOAK1;ip=>196.8.150.163 
#box=>MPLRDFDSOAK2;ip=>196.8.150.164 

use strict; 
use warnings; 

# include the library to allow easy access to command line arguments 
use Getopt::Long; 

# include the data dumper utility 
use Data::Dumper; 

my $usageInstructions = "Some instructions\n"; 
my $showMeTheInstructions = ""; 
my $iniFileToReadIn = ""; 
my @boxes; 

# read in the command line arguments 
GetOptions("ini=s" => \$iniFileToReadIn, 
    "H|h|?!" => \$showMeTheInstructions); 

if ($showMeTheInstructions) 
{ 
print $usageInstructions; 
exit 0; 
} 

readInINIFileIn($iniFileToReadIn, \@boxes) if ($iniFileToReadIn ne ""); 

print Dumper(\@boxes); 
print "\n\#\n\# END OF DATA DUMP\n\#\n\n"; 
exit 0; 

####################################################################################### 
# subroutine to read in the ini file and create the empty records for the boxes 
# specified 
sub readInINIFileIn 
{ 
my ($iniFile, $pointerToBoxes) = @_; 

my $noCRLFOnString = ""; 

# open the file 
open (ConfigFile, "<$iniFile") || die $!; 

# read in all the lines into an array 
my @configurationItems = <ConfigFile>; 

# close the file 
close (ConfigFile); 

# temporary record storage 
my %tempRecord; 

# create the defaults for all boxes 
my @LineHandlersRunning; 

my %tmpLineHandlerRunning = (LineHandlerName => "DEFAULT", 
    LineHandlerUpTime => 0, 
    NumberOfCommLinkDowns => 0, 
    NumberOfGaps => 0, 
    MemoryUsage => 0); 

push (@LineHandlersRunning, {%tmpLineHandlerRunning}); 

my %CurrentStatusInfo; 
my %PreviousStatusInfo; 

push @{ $CurrentStatusInfo{'LineHandlersRunning'} },   @LineHandlersRunning; 
push @{ $PreviousStatusInfo{'LineHandlersRunning'} },   @LineHandlersRunning; 

# loop through the config file and create the defaults for the database of boxes 
foreach my $configLine (@configurationItems) 
{ 
    my @TokenisedLineFromFileItems =(); 
    my @TokenisedLineFromFileNameValuePairs =(); 

    # store parameters 
    # each line will be ; separated then => separated, as in each one will have a number of items separated by ;'s and 
    # each item will be be a name & value pair separated by =>'s 
    @TokenisedLineFromFileItems = split(/;/,$configLine); 

    # remove quote marks around the outside of each element of the newly created array 
    s/^"|"$//g foreach @TokenisedLineFromFileItems; 

    # create information in database record to add to boxes 
    foreach my $NameValuePair (@TokenisedLineFromFileItems) 
    { 
    @TokenisedLineFromFileNameValuePairs = split(/=>/,$NameValuePair); 
    $noCRLFOnString = $TokenisedLineFromFileNameValuePairs[1]; 
    $noCRLFOnString =~ s/(\n|\r)//g; 

    $tempRecord{'Boxname'} = $noCRLFOnString if ($TokenisedLineFromFileNameValuePairs[0] eq "box"); 
    $tempRecord{'IPAddress'} = $noCRLFOnString if ($TokenisedLineFromFileNameValuePairs[0] eq "ip"); 
    } 

    # add all other defaults as blank 
    $tempRecord{'CurrentStatusInfo'} = {%CurrentStatusInfo}; 
    $tempRecord{'PreviousStatusInfo'} = {%PreviousStatusInfo}; 

    push(@$pointerToBoxes, {%tempRecord}); 
} 
} 
+0

它看起來你忘了結束一句話: 「隨後的任何條目都會被奇怪的創建(...)」。 – vezult 2009-10-06 14:02:42

+4

你知道,較長的變量名不一定是較好的變量名。 'TokenisedLineFromFileNameValuePairs',我的意思是,真的嗎?就目前而言,我的大腦無法理解代碼應該做什麼或者正在做什麼。您可以通過花點時間幫助您解決問題:確保Dumper輸出符合要求,確保有一個體面的輸入數據樣本,充分描述程序應該做什麼以及它實際上是什麼這樣做。 *怪異*不是一個好的描述,因爲它是在旁觀者的眼中。 – 2009-10-06 14:08:47

+0

http://thedailywtf.com/Articles/CodeThatDocumentsItselfSoWellItDoesNotNeedComments.aspx – 2009-10-06 14:13:08

回答

1

我猜這是因爲這兩行最終將相同的散列引用推送到兩個位置 - 所以如果你在一個位置改變hashref內容,另一個也會改變,這可能不是你想要的默認值值。

正如FM指出的那樣,這就是爲什麼您的Dumper輸出中有循環參考。

如果有人等着我打電話花了足夠長的時間,我會爲你重構你的代碼。

更新:好的,所以如果不知道完整的場景,很難說這是否是一個明智的方法。當然,你應該看看CPAN的各種INI解析模塊,但這裏是你的代碼的一個非常快的調整,讓您現有的邏輯結構到位:

use strict; 
use warnings; 

use Getopt::Long; 
use Data::Dumper; 

my $cmd_help = "Some instructions\n"; 
my $show_help = ""; 
my $ini_file_path = ""; 

# read in the command line arguments 
GetOptions("ini=s" => \$ini_file_path, 
      "H|h|?!" => \$show_help); 

if ($show_help) { 
    print $cmd_help; 
    exit 0; 
} 

if (! -f $ini_file_path) { 
    die "File '$ini_file_path' doesn't seem to exist."; 
} 

my $boxes = read_ini_file($ini_file_path); 

print Dumper($boxes); 

exit 0; 

=head2 read_ini_file 

read in the ini file and create the empty records for the boxes 

=cut 

sub read_ini_file { 
    my ($ini_file) = @_; 

    my @boxes; 

    my @config_lines; 
    { 
     # consider using File::Slurp 
     open (my $ini_fh, '<', $ini_file_path) || die $!; 

     @config_lines = <$ini_fh>; 
     chomp @config_lines; # remove \r\n 

     # file handle will close when $ini_fh goes out of scope 
    } 

    # create the defaults for all boxes 
    my %line_handlers_running_defaults = (LineHandlerName => "DEFAULT", 
              LineHandlerUpTime => 0, 
              NumberOfCommLinkDowns => 0, 
              NumberOfGaps => 0, 
              MemoryUsage => 0); 

    # loop through the config file and create the defaults for the database of boxes 
    foreach my $line (@config_lines) { 

     my %record; 

     my @token_pairs = map { s/^"//; s/^$//; $_ } split(/;/,$line); 

     # create information in database record to add to boxes 
     foreach my $pair (@token_pairs) { 
      my ($key, $val) = split(/=>/,$pair); 

      $record{Boxname} = $val if $key eq "box"; 
      $record{IPAddress} = $val if $key eq "ip"; 
     } 

     # add all other defaults as blank 
     $record{CurrentStatusInfo} = { LineHandlersRunning => [{%line_handlers_running_defaults}] }; 
     $record{PreviousStatusInfo} = { LineHandlersRunning => [{%line_handlers_running_defaults}] }; 

     push @boxes, \%record; 
    } 

    return \@boxes; 
} 

給出了這樣的輸出:

$VAR1 = [ 
     { 
    'IPAddress' => '196.8.150.163', 
    'CurrentStatusInfo' => { 
       'LineHandlersRunning' => [ 
           { 
            'NumberOfGaps' => 0, 
            'LineHandlerName' => 'DEFAULT', 
            'NumberOfCommLinkDowns' => 0, 
            'LineHandlerUpTime' => 0, 
            'MemoryUsage' => 0 
           } 
           ] 
        }, 
    'Boxname' => 'MPLRDFDSOAK1', 
    'PreviousStatusInfo' => { 
        'LineHandlersRunning' => [ 
           { 
            'NumberOfGaps' => 0, 
            'LineHandlerName' => 'DEFAULT', 
            'NumberOfCommLinkDowns' => 0, 
            'LineHandlerUpTime' => 0, 
            'MemoryUsage' => 0 
           } 
           ] 
       } 
     }, 
     { 
    'IPAddress' => '196.8.150.164', 
    'CurrentStatusInfo' => { 
       'LineHandlersRunning' => [ 
           { 
            'NumberOfGaps' => 0, 
            'LineHandlerName' => 'DEFAULT', 
            'NumberOfCommLinkDowns' => 0, 
            'LineHandlerUpTime' => 0, 
            'MemoryUsage' => 0 
           } 
           ] 
        }, 
    'Boxname' => 'MPLRDFDSOAK2', 
    'PreviousStatusInfo' => { 
        'LineHandlersRunning' => [ 
           { 
            'NumberOfGaps' => 0, 
            'LineHandlerName' => 'DEFAULT', 
            'NumberOfCommLinkDowns' => 0, 
            'LineHandlerUpTime' => 0, 
            'MemoryUsage' => 0 
           } 
           ] 
       } 
     } 
    ]; 
+0

謝謝馬克 你的例子和幫助非常感謝,因爲它告訴我我哪裏出了錯,實際上給了一個指示如何糾正,甚至更有幫助的是,簡化我的代碼。再次感謝你,祝你有美好的一天。 親切的問候 格倫 – Glen 2009-10-12 11:44:23

3

我沒有耐心,通過所有的代碼涉水,但我敢打賭你的問題與Data::Dumper輸出的這個方面有關:

$VAR1->[0]{'CurrentStatusInfo'}{'LineHandlersRunning'}[0] 

換句話說,您的數據結構包含對結構其他部分的引用。

也許你認爲你正在製作一部分數據結構的副本,但是你得到的是淺拷貝而不是深拷貝?例如,我懷疑這個代碼:

$tempRecord{'CurrentStatusInfo'} = {%CurrentStatusInfo}; 
$tempRecord{'PreviousStatusInfo'} = {%PreviousStatusInfo}; 

如果確實問題與淺拷貝,在Clone模塊可能的幫助。

2

使用詞法文件句柄,聲明變量的最小可能範圍。我不知道你的問題是什麼,但它很可能是由一些變量持續時間比你想象的長。