用哈希数组填充哈希数组

发布于 2024-08-06 14:42:22 字数 5322 浏览 5 评论 0原文

我目前正在开发一款监控软件,它采用服务器名称和 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});
 }
}

I am currently developing a piece of monitoring software that takes an input file of server names and ip addresses and creates a rudimentary database of information. I want to default some values as it processes the config file and it works fine for the first time round the loop but any subsequent entries get created with weird (well weird to me was the best way to describe it as it is probably correct and the code is wrong, as in the code is doing exactly what i have asked it to do but not necessarily what i want it to do).

the output from the code below looks like:

$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'}
                                }
      }
    ];

The following is the code:

#######################################################################################
# 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});
 }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

柠北森屋 2024-08-13 14:42:22

我没有耐心仔细阅读您的所有代码,但我敢打赌您的问题与 Data::Dumper 输出的这方面有关:

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

换句话说,您的数据结构包含对结构其他部分的引用。

也许您认为您正在复制部分数据结构,但实际上您获得的是浅副本而不是深副本?例如,我对这段代码表示怀疑:

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

如果问题确实与浅复制有关,则 Clone< /a> 模块可能会有所帮助。

I don't have the patience to wade through all of your code, but I'll bet your problem is related to this aspect of the Data::Dumper output:

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

In other words, your data structure contains a reference to other parts of the structure.

Perhaps you think you are making a copy of part of the data structure, but instead you are getting a shallow copy rather than a deep copy? For example, I'm suspicious of this code:

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

If indeed the problem is related to shallow copying, the Clone module might help.

拔了角的鹿 2024-08-13 14:42:22

使用词法文件句柄,在尽可能小的范围内声明变量。我不知道你的问题是什么,但这很可能是由某些变量持续时间比你想象的要长引起的。

Use lexical filehandles, declare variables in the smallest possible scope. I do not know what your problem is, but it is most likely caused by some variable persisting longer than you think it does.

生死何惧 2024-08-13 14:42:22

我猜测这是因为这两行最终将相同的哈希引用推入两个位置 - 因此,如果您更改一个位置中的 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
                                 }
                               ]
                }
      }
    ];

I'm guessing it's because these two lines end up pushing the same hash reference into two locations - so if you alter the hashref contents in one location, the other will change as well which is probably not what you want for default values.

As FM pointed out, this is why you have the circular reference in your Dumper output.

If someone I'm waiting to get off the phone takes long enough i'll refactor your code for you.

Update: ok, so without knowing the full scenario it's hard to say if this is a sensible approach. certainly you should look at the various INI parsing modules in CPAN, but here is a very quick tweak of your code, leaving your existing logic structure in place:

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;
}

gives this output:

$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
                                 }
                               ]
                }
      }
    ];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文