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

Zac*_*ada 5 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.
 }
Run Code Online (Sandbox Code Playgroud)

检查日期的代码(先前设置,今天为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";
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢!

Ilm*_*nen 8

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

$SCAN{$key} = \@data;
Run Code Online (Sandbox Code Playgroud)

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

有几种方法可以解决这个问题.也许最简单的方法是通过将上面的行改为,使代码分配对数组副本的引用@data$SCAN{$key}

$SCAN{$key} = [ @data ];
Run Code Online (Sandbox Code Playgroud)

或者,您可以重写整个循环以使用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;
}
Run Code Online (Sandbox Code Playgroud)

但是,你真正应该做的,而不仅仅是修复这个特定bug的症状,就是要了解变量作用域如何在Perl中运行以及如何使用它,并相应地重写代码.

特别是,看你的代码,你不使用,我非常可疑strict编译的代码.如果你想编写干净的Perl代码,你真正应该做的第一件事就是#!在行之后立即将以下两行添加到你的所有脚本中:

use strict;
use warnings;
Run Code Online (Sandbox Code Playgroud)

strict编译迫使你避免某些不良且易出错的习惯,比如使用符号引用或未经申报的全局变量,而warnings编译做出解释警告你其他各种愚蠢的,危险的,不明确的或其他不良的东西(你真的应该对待作为错误并修复,直到你不再收到警告).

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

foreach my $key (keys %IPS) {
Run Code Online (Sandbox Code Playgroud)

要么

while (my $line = <>) {
Run Code Online (Sandbox Code Playgroud)

PS.我还注意到你向我们展示的代码中有一个令人担忧的评论:

# repeat of the above code, but with ...
Run Code Online (Sandbox Code Playgroud)

一般情况下,那种重复的代码应该是一个大的闪烁信号,表明你可能做错事-编程的黄金法则就是" 不要重复自己. "

当然,也那几个,非常非常罕见的情况下,您需要基本上做同样的事情在两种不同的方式,但有这么多的小和任意的差异贯穿充塞它的清洁写了整个事情的两倍.但是如果在这种情况下我会感到非常惊讶 - 我打赌你只能编写一次代码,而且可能只是插入一个

if (not $init and exists ...) {
Run Code Online (Sandbox Code Playgroud)

检查在合适的位置.