2012-07-26 13 views
5

Estoy trabajando en una secuencia de comandos de Perl para ayudar con la automatización del escaneo de máquinas en nuestra red. No soy un programador de oficio, pero a pesar de todo este proyecto me ha sido asignado, y estoy bastante perplejo. Antes de explicar la naturaleza de lo que me está afectando, permítanme explicar el esquema de lo que estoy haciendo.Perl ¿cambia el valor dentro de un condicional antes de ingresar el condicional?

Básicamente, este script se ejecutará cada n horas. Cuando se ejecuta, comprobará un archivo que contenga un registro de direcciones IP activas y las comparará con un registro DHCP para seleccionar solo aquellas que son estáticas. Estos se colocan en un hash (uno nuevo si está marcado para inicializar, cargado utilizando Storable de lo contrario), con la clave siendo el IP y dentro de un array su MAC [0] y una fecha "último escaneado" [1] inicialmente configurada para 19700101. La siguiente parte del script compara la fecha entre la fecha de hoy y la fecha de "último escaneado" y, si está por debajo de un cierto umbral, envía una consulta a nuestro escáner.

El problema que me tiene tan perdido es que cuando se está verificando la fecha, me parece que el valor de fecha (la última actualización "escaneada") se está configurando antes de ingresar el condicional. Si bien esto no me parece probable, es la única posibilidad que se me ocurre. Aquí es los trozos relevantes de código:

El código que añade IP/MAC con el hash

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

El código que comprueba la fecha (que se ha establecido previamente, y sería 20.120.726 por hoy). Entre el código anterior y la siguiente hay nada más que comentarios

$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"; 

Ahora, la razón por la que creo que el valor se cambia antes de entrar en el bucle es cuando agrego comunicado una 'impresión $ lastScanned' justo antes de la 'if (($ date ...) {' la fecha imprimió lo que está asignado a $ date before - pero si quiero comentar la declaración '$ SCAN {$ key} [1] = $ date;', la impresión las declaraciones imprimirán la fecha '19700101' y todo funciona como debería. ¿Qué está sucediendo? $ SCAN {$ key} [1] nunca se toca excepto en los dos lugares mostrados arriba.

Disculpe si esto es muy grave expresado, o no tiene sentido. Hice mi mejor esfuerzo para explicar algo que ha sido acosándome por horas.

¡Gracias!

Respuesta

8

Debido a que la matriz @data es global, cada vez que ejecute la instrucción

$SCAN{$key} = \@data; 

estás asignando a $SCAN{$key} una referencia a la misma @data matriz. Por lo tanto, todos los valores en %SCAN terminan apuntando a la misma matriz, que presumiblemente no es la que desea.

Existen varias formas de solucionarlo.Tal vez el más simple sería la de hacer que el código asignar una referencia a una copia de la matriz @data a $SCAN{$key}, cambiando la línea anterior a

$SCAN{$key} = [ @data ]; 

Como alternativa, puede reescribir todo el circuito a utilizar una matriz léxica declarada con my dentro del bucle — de esa manera se crea una nueva matriz separada en cada iteración:

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

Sin embargo, lo que realmente debe hacer, en lugar de sólo f Ixing los síntomas de este error en particular, es aprender how variable scoping works in Perl y cómo se debe utilizar, y reescribir el código en consecuencia.

En particular, mirando su código, sospecho mucho que no está usando the strict pragma en su código. Si desea escribir código Perl limpio, lo primera que realmente debe hacer es anteponer los siguientes dos líneas a todas las secuencias de comandos, inmediatamente después de la línea #!:

use strict; 
use warnings; 

Los strict fuerzas pragma que evitar ciertos malos hábitos propensos a errores, como el uso de referencias simbólicas o variables globales no declaradas, mientras que the warnings pragma hacen que el intérprete te advierta sobre otras cosas tontas, arriesgadas, ambiguas o indeseables (que realmente deberías tratar como errores y corregir hasta que no obtengas más advertencias).

Por supuesto, esto no quiere decir que sólo debe declarar todas las variables al comienzo de la secuencia de comandos con my (o our) sólo para hacer strict feliz. En cambio, lo que debe hacer es mirar cada variable, ver dónde se usa realmente y declararla en el ámbito más interno en el que se necesita. (Si está reutilizando el mismo nombre de variable en diferentes partes del código, trátelo como separado declarar variables y cada uno de ellos por separado.) Recuerde que puede declarar variables de bucle en la sentencia de bucle, como en

foreach my $key (keys %IPS) { 

o

while (my $line = <>) { 

Sal. También he notado un comentario preocupante en el código que nos mostró:

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

En general, este tipo de duplicación de código debería ser una señal intermitente grande que probablemente estás haciendo algo mal — la regla de oro de la programación es "Don't repeat yourself. "

por supuesto, hay son esos pocos, muy, muy raras ocasiones en las que haces necesidad de hacer esencialmente la misma cosa de dos maneras diferentes, pero con tantas diferencias pequeñas y arbitrarias salpicadas a lo largo que es más limpio para escribe todo el asunto dos veces. Pero estaría muy sorprendido si ese era el caso aquí — apuesto a que podría escribir ese código sólo una vez, y sólo tal vez inserta un cheque

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

en un lugar adecuado.

+0

¡Gracias! ¡Esto fue muy útil! ¡Leeré todo lo que vinculó y revisaré esa sección de código tan inquietante! –

3

Como dice Ilmari, su problema es que todos los elementos de %SCAN puntos a la misma serie de dos elementos que se @data en el primer bloque de código, por lo $SCAN{<anything>}[1] es la misma variable para todas las direcciones IP.

Para solucionar esto, mi preferencia sería olvidarse de @data y escribir

$SCAN{$key} = [ $IPS{$key}, '19700101' ]; 

que genera una nueva matriz anónima cada vez que se ejecuta la sentencia y asigna una referencia a él como el valor del hash.

Tenga en cuenta también que he utilizado una cadena para la fecha, ya que no puede escribir cosas como: fecha de $date - $lastScanned aritmética es más complejo que eso. Restando 31-JAN-2012 de 1-FEB-2012 se convertiría en 20120201 - 20120131 o 70!

Afortunadamente hay módulos para hacer esto más fácil y puede usar el módulo Time::Piece, que es un módulo central (es decir, se instala con Perl estándar desde Perl v5.9) y le permitirá hacer este tipo de operaciones aritméticas.

En la parte superior de su programa, después de use strict y use warnings, se escribe

use Time::Piece; 

y más tarde, por su tiempo inicial, escribe

my $initial = localtime(0); 

y luego

my $date = localtime; 

Puede ver las fechas a las que corresponden los dos valores simplemente imprimiendo th em

print $initial, "\n"; 
print $date, "\n"; 

que mostrará algo así como

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

y una resta simple que da la verdadera diferencia, en cuestión de segundos

print $date - $initial; 

Así que si $threshold es en día se puede comprobar el intervalo escribiendo

if ($date - $lastScanned > $threshold * 24 * 60 * 60) { ... } 

Espero no haberte asustado aquí, pero necesitaba cambiar y pensé que deberías saberlo. El módulo hará mucho más que esto, y si desea ver la documentación es here. Y por favor haz otra pregunta si te quedas atascado.