我目前正在开发一种监控软件,它采用服务器名称和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 :(得分:3)
我没有耐心去浏览你的所有代码,但我敢打赌你的问题与Data::Dumper
输出的这个方面有关:
$VAR1->[0]{'CurrentStatusInfo'}{'LineHandlersRunning'}[0]
换句话说,您的数据结构包含对结构其他部分的引用。
也许你认为你正在复制部分数据结构,但是你得到的是浅拷贝而不是深拷贝?例如,我怀疑这段代码:
$tempRecord{'CurrentStatusInfo'} = {%CurrentStatusInfo};
$tempRecord{'PreviousStatusInfo'} = {%PreviousStatusInfo};
如果确实问题与浅层复制有关,Clone模块可能有帮助。
答案 1 :(得分:2)
使用词法文件句柄,在尽可能小的范围内声明变量。我不知道你的问题是什么,但很可能是因为某些变量的持续时间比你想象的要长。
答案 2 :(得分: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
}
]
}
}
];