Perl在输入条件之前在条件内改变值?

时间:2012-07-26 21:58:40

标签: arrays perl hashmap conditional-statements

我正在开发一个Perl脚本,用于帮助扫描网络上的计算机。我不是一个贸易程序员,但是这个项目已经分配给我了,我很难过。在我解释困扰我的本质之前,让我解释一下我在做什么的大纲。

基本上,这个脚本每n小时运行一次。运行时,它将检查包含活动IP日志的文件,并根据DHCP日志检查它们,以仅挑出那些静态IP。然后将这些放入哈希(如果标记为初始化则为新的,否则使用Storable加载),密钥为IP,并且在数组内,其MAC [0]和“最后扫描”日期[1]初始设置为脚本的下一部分将比较今天日期和“上次扫描”日期之间的日期 - 如果它在某个阈值之下,它会向我们的扫描仪发送查询。

让我失去的问题是,在检查日期时,在我看来,在输入条件之前,正在设置日期值(更新的“上次扫描”)。虽然这对我来说似乎不太可能,但这是我能想到的唯一可能性。以下是相关的代码块:

将IP / MAC添加到哈希

的代码
 if(init == 1){
            %SCAN = ();

            @data = ();

            foreach $key (keys %IPS){

                    $unsavedDB = 1;

                    $data[0] = $IPS{$key};
                    $data[1] = 19700101;

                    print $data[1];

                    $SCAN{$key} = \@data;
            }
 }else{
            #repeat of the above code, but with a if(exists...) to prevent duplicates from being added to the hash that is loaded via storables.
 }

检查日期的代码(先前设置,今天为20120726)。在上面的代码和以下代码之间只有注释

    $scanned = 0;

    foreach $key (keys %SCAN){

            $lastScanned = $SCAN{$key}[1];

            if(($date - $lastScanned) > $threshold){
                    $unsavedDB = 1;

                    $toScan = ${$key}[0];

                    #omitted data for security reasons, just basically forms a string to send to a scanner

                    $SCAN{$key}[1] = $date;

                    $scanned++;
            }
    }

    print "finished. $scanned hosts queued\n";

现在,我认为在进入循环之前更改了值的原因是我在'if(($ date ...){'日期打印之前添加'print $ lastScanned'语句无论之前分配给$ date的是什么 - 但是如果我要注释'$ SCAN {$ key} [1] = $ date;'语句,print语句将打印'19700101'日期,一切都按预期运行。正在发生什么?除了上面显示的两个地方外,$ SCAN {$ key} [1]永远不会被触及。

对不起,如果措辞非常严厉,或者没有意义。我尽力解释一直困扰我几个小时的事情。

谢谢!

2 个答案:

答案 0 :(得分:8)

因为您的@data数组是全局的,所以每次执行语句

$SCAN{$key} = \@data;

您要为$SCAN{$key}分配对相同 @data数组的引用。因此,%SCAN中的所有值最终都指向同一个数组,这可能不是您想要的。

有几种方法可以解决这个问题。也许最简单的方法是通过将上面的行更改为

,使代码将@data数组的副本的引用分配给$SCAN{$key}
$SCAN{$key} = [ @data ];

或者,您可以重写整个循环以使用循环内部用my声明的词法数组 - 这样您就可以在每次迭代时创建一个新的独立数组:

foreach $key (keys %IPS) {
        $unsavedDB = 1;

        my @data;  # <--- this line is new!

        $data[0] = $IPS{$key};
        $data[1] = 19700101;

        print $data[1];

        $SCAN{$key} = \@data;
}

然而,你真正应该做的事情,而不仅仅是修复这个特定错误的症状,是学习how variable scoping works in Perl以及如何使用它,并相应地重写你的代码。< / p>

特别是,查看代码时,我非常怀疑您在代码中没有使用the strict pragma。如果你想编写干净的Perl代码,你真正应该做的第一个事情就是在#!行之后立即将以下两行添加到你的所有脚本中:

use strict;
use warnings;

strict pragma强迫你避免某些容易出错和容易出错的习惯,例如使用符号引用或未声明的全局变量,而the warnings pragma会让翻译人员警告你其他各种愚蠢的,有风险的,模棱两可或其他不受欢迎的事情(你真的应该把它视为错误并修复,直到你不再收到警告为止)。

当然,这并不意味着您应该只使用my(或our)在脚本开头声明所有变量,以使strict满意。相反,你应该做的是查看每个变量,看看它实际使用的位置,并在需要的最里面的范围内声明它。(如果你在代码的不同部分重用相同的变量名,那么将它们视为单独的变量并分别声明每个变量。)请记住,您可以在循环语句中声明循环变量,如

foreach my $key (keys %IPS) {

while (my $line = <>) {

聚苯乙烯。我还注意到你向我们展示的代码中有一个令人担忧的评论:

# repeat of the above code, but with ...

一般来说,这种代码重复应该是一个很大的闪烁信号,你可能做错了 - 编程的黄金法则是“Don't repeat yourself.

当然, 任意的差异充满了整个事情两次写得更清楚。但是如果在这种情况下我会感到非常惊讶 - 我打赌你只能编写一次该代码,并且可能只是插入一个

if (not $init and exists ...) {

在合适的位置检查。

答案 1 :(得分:3)

正如Ilmari所说,你的问题是%SCAN的每个元素都指向第一个代码块中@data相同的双元素数组,所以{{ 1}}是所有IP地址的相同变量。

要解决此问题,我的偏好是忘记$SCAN{<anything>}[1]并写

@data

每次执行语句时都会生成一个新的匿名数组,并将其作为哈希值分配给它。

另请注意,我已使用字符串作为日期,因为无法编写$SCAN{$key} = [ $IPS{$key}, '19700101' ]; 之类的内容:日期算术比此更复杂。从$date - $lastScanned中减去31-JAN-2012将变为1-FEB-2012或70!

幸运的是有一些模块可以使这更容易,你可以使用模块20120201 - 20120131,这是一个核心模块(即它从Perl v5.9开始安装标准的Perl)并且可以让你做这种排序算术。

在您的计划顶部,在Time::Pieceuse strict之后,您需要撰写

use warnings

然后在你的初始时间写下

use Time::Piece;

然后

my $initial = localtime(0);

您可以通过打印它们来查看两个值对应的日期

my $date = localtime;

将显示类似

的内容
print $initial, "\n";
print $date, "\n";

和简单的减法为您提供真正的差异,以秒为单位

Thu Jan  1 00:00:00 1970
Fri Jul 27 01:40:53 2012

因此,如果print $date - $initial; 以天为单位,您可以通过编写

来检查间隔
$threshold

我希望我没有在这里吓到你,但它需要改变,我想你应该知道。该模块将做很多事情,如果你想查看文档here。如果你遇到问题,请再问一个问题。