2010-10-22 15 views
32

So che il blocco BEGIN è compilato ed eseguito prima del corpo principale di un programma Perl. Se non siete sicuri di che basta provare a eseguire il comando perl -cw su questo:Qual è il ruolo del blocco BEGIN in 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"; 
} 

Mi è stato insegnato che la compilazione precoce e l'esecuzione di un blocco BEGIN permette un programmatore in modo che le risorse necessarie siano disponibili prima di il programma principale viene eseguito.

E così ho utilizzato i blocchi BEGIN per assicurarmi che cose come le connessioni DB siano state stabilite e siano disponibili per l'uso dal programma principale. Allo stesso modo, utilizzo i blocchi END per garantire che tutte le risorse siano chiuse, cancellate, terminate, ecc. Prima che il programma termini.

Dopo una discussione stamattina, mi chiedo se questo sia il modo sbagliato di esaminare i blocchi BEGIN e END.

Qual è il ruolo previsto di un blocco BEGIN in Perl?

Aggiornamento 1: Ho appena scoperto perché la connessione DBI non ha funzionato. Dopo essere stato dato questo piccolo programma Perl:

use strict; 
use warnings; 

my $x = 12; 

BEGIN { 
    $x = 14; 
} 

print "$x\n"; 

quando eseguito la stampa 12.

Aggiornamento 2: Grazie al commento di Eric Strom di sotto di questa nuova versione rende più chiaro:

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

e l'output è

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

Acceso Di nuovo, grazie Eric!

+2

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

+0

Se questo è il caso, perché non posso creare una connessione a un DBI DB all'interno di un blocco BEGIN utilizzando la funzione DBI connect() standard? Se rimuovo il blocco BEGIN, viene effettuata la connessione –

+6

Per il tuo aggiornamento: questo perché il 'my $ x = 12' dichiara la variabile al momento della compilazione ma non esegue il compito. Quindi viene eseguito il blocco di inizio che assegna 14. Quindi, quando il programma inizia a funzionare, viene eseguito l'assegnazione di 'my' line che ti dà 12. –

risposta

21

Hai provato a scambiare il blocco BEGIN{} per un blocco INIT{}? Questo è l'approccio standard per cose come modperl che usano il modello "compile-once, run-many", dato che è necessario inizializzare di nuovo le cose su ogni corsa separata, non solo una volta durante la compilazione.

Ma devo chiedere perché è tutto in blocco speciale comunque. Perché non fai semplicemente una specie di funzione prepare_db_connection(), e poi la chiami come vuoi quando il programma si avvia?

Qualcosa che non funziona in uno BEGIN{} avrà anche lo stesso problema se è il codice di linea principale in un file di modulo che ottiene use d. Questo è un altro possibile motivo per utilizzare un blocco INIT{}.

Ho visto anche mortali-abbracciare problemi di reciproco ricorsione che devono essere svelati usando qualcosa come un require invece di use, o un INIT{} al posto di un BEGIN{}. Ma è piuttosto raro.

Considerate questo programma:

% 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" } 

Quando compilato solo, 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 

Mentre quando compilato e eseguito, produce questo:

% 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 

E il la shell riporta un'uscita di 255, per lo die.

Dovresti essere in grado di organizzare che la connessione avvenga quando ne hai bisogno, anche se uno BEGIN{} si rivela troppo presto.

Hm, appena ricordato. Non c'è possibilità che tu stia facendo qualcosa con DATA in un BEGIN{}, c'è? Questo non è impostato finché l'interprete non viene eseguito; non è aperto al compilatore.

31

Mentre i blocchi BEGIN e 10 possono essere utilizzati come descritto, l'utilizzo tipico è apportare modifiche che influiscono sulla compilazione successiva.

Ad esempio, l'istruzione use Module qw/a b c/; realtà significa:

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

analogamente, la dichiarazione subroutine sub name {...} è in realtà:

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

Poiché questi blocchi sono eseguiti in fase di compilazione, tutte le righe che vengono compilati dopo l'esecuzione di un blocco utilizzerà le nuove definizioni create dai blocchi BEGIN. In questo modo è possibile chiamare subroutine senza parentesi o in che modo i vari moduli "cambiano il modo in cui il mondo funziona".

END i blocchi possono essere utilizzati per pulire le modifiche apportate ai blocchi BEGIN ma è più comune utilizzare gli oggetti con un metodo DESTROY.

Se lo stato che si sta tentando di eliminare è una connessione DBI, è possibile farlo in un blocco END. Non creerei la connessione in un blocco BEGIN per diversi motivi. Di solito non è necessario che la connessione sia disponibile in fase di compilazione. L'esecuzione di azioni come la connessione a un database in fase di compilazione rallenterà drasticamente qualsiasi editor che si utilizza per il controllo della sintassi (poiché viene eseguito perl -c).

+0

Grazie Eric. Non ero sicuro di aggiungere un avvertimento alla mia domanda sull'uso dei blocchi END in un programma non OO. –

3

Mentre le altre risposte sono vere, lo trovo anche la pena di menzionare l'uso di BEGIN e END blocca quando si utilizzano i -n o -p interruttori a Perl.

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

Quando si utilizzano gli switch -n e -p per Perl, BEGIN e END lavoro proprio come fanno in awk, come caso degenere.

Per chi non conosce l'interruttore -n, si dice Perl per avvolgere il programma con:

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

http://perldoc.perl.org/perlrun.html#Command-Switches se siete interessati su informazioni più specifiche sulle opzioni Perl.

Come esempio per illustrare l'utilizzo del BEGIN con l'interruttore -n, questo Perl one-liner enumera le righe del comando ls:

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

In questo caso, il BEGIN -block viene utilizzato per avviare la variabile $i impostandola su 1 prima di elaborare le righe di ls. Questo esempio visualizzerà qualcosa di simile:

1: foo.txt 
2: bar.txt 
3: program.pl 
4: config.xml 
Problemi correlati