2010-10-22 9 views
32

Sé que el bloque BEGIN se compila y ejecuta antes que el cuerpo principal de un programa Perl. Si no está seguro de que acaba de intentar ejecutar el comando perl -cw sobre esto:¿Cuál es el rol del bloque BEGIN en Perl?

#!/ms/dist/perl5/bin/perl5.8 

use strict; 
use warnings; 

BEGIN { 
    print "Hello from the BEGIN block\n"; 
} 

END { 
    print "Hello from the END block\n"; 
} 

me han enseñado que la compilación temprana y la ejecución de un bloque BEGIN permite a un programador asegurar que todos los recursos necesarios estén disponibles antes el programa principal se ejecuta.

Y por lo que he estado usando COMENZAR bloques para asegurarse de que cosas como las conexiones de base de datos se han establecido y están disponibles para su uso por el programa principal. De manera similar, utilizo bloques END para asegurarme de que todos los recursos estén cerrados, eliminados, finalizados, etc. antes de que el programa finalice.

Después de una discusión de esta mañana, me pregunto si esto de manera equivocada que mirar BEGIN y END.

¿Cuál es el papel previsto de un bloque BEGIN en Perl?

Actualización 1: Acabo de descubrir por qué la conexión DBI no funcionó. Después de recibir este pequeño programa Perl:

use strict; 
use warnings; 

my $x = 12; 

BEGIN { 
    $x = 14; 
} 

print "$x\n"; 

cuando se ejecuta imprime 12.

Actualización 2: Gracias al comentario de Eric Strom por debajo de esta nueva versión hace que sea más claro:

use strict; 
use warnings; 

my $x = 12; 
my $y; 

BEGIN { 
    $x = 14; 
    print "x => $x\n"; 
    $y = 16; 
    print "y => $y\n"; 
} 

print "x => $x\n"; 
print "y => $y\n"; 

y la salida es

x => 14 
y => 16 
x => 12 
y => 16 

Encendido ¡Otra vez, gracias Eric!

+2

http://www.compuspec.net/reference/language/perl/BEGIN_and_END.shtml – jantimon

+0

Si ese es el caso, ¿por qué no puedo crear una conexión a una DBI DB dentro de un bloque BEGIN usando la función estándar DBI connect()? Si elimino el bloque BEGIN, la conexión se hace –

+6

Para su actualización: eso es porque el 'mi $ x = 12' declara la variable en tiempo de compilación pero no realiza la asignación. Luego comienza el ciclo de ejecución que asigna 14. Luego, cuando el programa comienza a ejecutarse, la asignación de la línea 'my' se ejecuta y le da 12. –

Respuesta

21

¿Ha intentado cambiar el bloque BEGIN{} por un bloque INIT{}? Ese es el enfoque estándar para cosas como modperl que utiliza el modelo "compilar una vez, ejecutar muchas", ya que necesita reiniciar las cosas en cada ejecución por separado, no solo una vez durante la compilación.

Pero tengo que preguntarme por qué está todo en un bloque especial de todos modos. ¿Por qué no hace una especie de función prepare_db_connection() y luego la llama cuando lo necesita cuando se inicia el programa?

Algo que no funcionará en un BEGIN{} también tendrá el mismo problema si es código de línea principal en un archivo de módulo que obtiene use d. Esa es otra posible razón para usar un bloque INIT{}.

También he visto mortal-abrazar problemas de recursión mutua que tienen que ser descifrado usando algo así como un lugar de requireuse, o un INIT{} en lugar de un BEGIN{}. Pero eso es bastante raro.

Considere este programa:

% cat sto-INIT-eg 
#!/usr/bin/perl -l 
print    " PRINT: main running"; 
die     " DIE: main dying\n"; 
die     "DIE XXX /* NOTREACHED */"; 
END   { print "1st END: done running" } 
CHECK  { print "1st CHECK: done compiling" } 
INIT  { print "1st INIT: started running" } 
END   { print "2nd END: done running" } 
BEGIN  { print "1st BEGIN: still compiling" } 
INIT  { print "2nd INIT: started running" } 
BEGIN  { print "2nd BEGIN: still compiling" } 
CHECK  { print "2nd CHECK: done compiling" } 
END   { print "3rd END: done running" } 

Cuando compilado solamente, que produce:

% perl -c sto-INIT-eg 
1st BEGIN: still compiling 
2nd BEGIN: still compiling 
2nd CHECK: done compiling 
1st CHECK: done compiling 
sto-INIT-eg syntax OK 

Mientras que cuando se compila y ejecutados, se produce esto:

% perl sto-INIT-eg 
1st BEGIN: still compiling 
2nd BEGIN: still compiling 
2nd CHECK: done compiling 
1st CHECK: done compiling 
1st INIT: started running 
2nd INIT: started running 
    PRINT: main running 
    DIE: main dying 
3rd END: done running 
2nd END: done running 
1st END: done running 

Y el shell informa una salida de 255, por el die.

Debería poder hacer los arreglos para que la conexión se realice cuando lo necesite, incluso si un BEGIN{} resulta demasiado temprano.

Hm, acabo de recordar. No hay posibilidad de que esté haciendo algo con DATA en un BEGIN{}, ¿verdad? Eso no está configurado hasta que el intérprete se ejecuta; no está abierto para el compilador.

31

Mientras que los bloques BEGIN y END se pueden usar como usted describe, el uso típico es hacer cambios que afectan la compilación posterior.

Por ejemplo, la declaración use Module qw/a b c/; en realidad significa:

BEGIN { 
    require Module; 
    Module->import(qw/a b c/); 
} 

Del mismo modo, la declaración de subrutina sub name {...} es en realidad:

BEGIN { 
    *name = sub {...}; 
} 

Dado que estos bloques se ejecutan en tiempo de compilación, todas las líneas que se compilan después de ejecutar un bloque, se usarán las nuevas definiciones creadas por los bloques BEGIN. Así es como puede llamar a las subrutinas sin paréntesis, o cómo varios módulos "cambian la forma en que funciona el mundo".

END bloques se pueden utilizar para limpiar los cambios que han realizado los bloques BEGIN pero es más común usar objetos con un método DESTROY.

Si el estado que está tratando de limpiar es una conexión DBI, hacerlo en un bloque END está bien. Sin embargo, no crearía la conexión en un bloque BEGIN por varias razones. Por lo general, no es necesario que la conexión esté disponible en el momento de la compilación. Realizar acciones como conectarse a una base de datos en tiempo de compilación ralentizará drásticamente cualquier editor que use que tenga comprobación de sintaxis (porque eso ejecuta perl -c).

+0

Gracias Eric. No estaba seguro acerca de agregar una advertencia a mi pregunta sobre el uso de bloques END en un programa no OO. –

3

Mientras que las otras respuestas son verdaderas, lo encuentro también vale la pena mencionar el uso de BEGIN y END bloques cuando se utilizan los interruptores -n o -p a Perl.

De http://perldoc.perl.org/perlmod.html

Al utilizar los interruptores y -n -p para Perl, BEGIN y END trabajo tal como lo hacen en AWK, como un caso degenerado.

Para aquellos no familiarizados con el interruptor -n, se dice Perl para envolver el programa con:

while (<>) { 
    ... # your program goes here 
} 

http://perldoc.perl.org/perlrun.html#Command-Switches si usted está interesado en adquirir información más específica acerca de los modificadores de Perl.

como un ejemplo para demostrar el uso de BEGIN con el interruptor -n, esta Perl de una sola línea enumera las líneas del comando ls:

ls | perl -ne 'BEGIN{$i = 1} print "$i: $_"; $i += 1;' 

En este caso, la -bloque BEGIN se utiliza para iniciar la variable $i configurándola en 1 antes de procesar las líneas de ls. Este ejemplo algo salida como:

1: foo.txt 
2: bar.txt 
3: program.pl 
4: config.xml 
Cuestiones relacionadas