我正在开发一个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]永远不会被触及。
对不起,如果措辞非常严厉,或者没有意义。我尽力解释一直困扰我几个小时的事情。
谢谢!
答案 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::Piece
和use 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。如果你遇到问题,请再问一个问题。