簡體   English   中英

BEGIN塊在Perl中起什么作用?

[英]What is the role of the BEGIN block in Perl?

我知道BEGIN塊是在Perl程序的主體之前編譯和執行的。 如果不確定這一點,請嘗試運行命令perl -cw:

#!/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";
}

有人告訴我,早期編譯和執行BEGIN塊可以使程序員確保在執行主程序之前可以使用任何需要的資源。

因此,我一直在使用BEGIN塊來確保已經建立了諸如DB連接之類的東西,並且可供主程序使用。 同樣,我使用END塊來確保在程序終止之前關閉,刪除,終止所有資源。

經過今天早上的討論之后,我想知道這是否是看待BEGIN和END塊的錯誤方法。

BEGIN塊在Perl中的預期作用是什么?

更新1:剛剛找出了為什么DBI連接不起作用。 得到這個小的Perl程序后:

use strict;
use warnings;

my $x = 12;

BEGIN {
    $x = 14;
}

print "$x\n";

執行時將打印12。

更新2:感謝Eric Strom在此新版本下面的評論使它更清晰:

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

輸出是

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

再次感謝Eric!

雖然可以按照您的描述使用BEGINEND塊,但是通常的用法是進行會影響后續編譯的更改。

例如, use Module qw/abc/; 聲明實際上意味着:

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

同樣,子例程聲明sub name {...}實際上是:

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

由於這些塊是在編譯時運行的,因此,在塊運行后編譯的所有行都將使用BEGIN塊所做的新定義。 這是您可以在不帶括號的情況下調用子例程的方式,或者各種模塊如何“改變世界的工作方式”。

END塊可用於清除BEGIN塊所做的更改,但更常見的是將對象與DESTROY方法一起使用。

如果您要清除的狀態是DBI連接,則可以在END塊中執行此操作。 盡管出於多種原因,我不會在BEGIN塊中創建連接。 通常,不需要在編譯時使用連接。 執行諸如在編譯時連接到數據庫之類的操作將大大減慢您使用的所有具有語法檢查的編輯器的速度(因為該編輯器運行perl -c )。

您是否嘗試過將BEGIN{}塊換成INIT{}塊? 這是諸如modperl之類的使用“一次編譯,多次運行”模型的標准方法,因為您需要在每次單獨運行時重新初始化,而不僅僅是在編譯過程中一次。

但是我必須問為什么無論如何都是如此。 為什么不制作某種prepare_db_connection()函數,然后在程序啟動時根據需要調用它呢?

如果在BEGIN{}不起作用的某些東西,如果它是use d的模塊文件中的主代碼,也會遇到同樣的問題。 這是使用INIT{}塊的另一個可能原因。

我還看到了相互遞歸的致命問題,必須使用require而不是use ,或者使用INIT{}而不是BEGIN{} 但這非常罕見。

考慮以下程序:

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

僅編譯時,它會產生:

% 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

在編譯執行時,它會產生以下結果:

% 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

並且每個die ,shell報告退出值為255。

即使BEGIN{}證明為時過早,您也應該能夠安排在需要時進行連接。

嗯,只是想起了。 您不可能對BEGIN{} DATA進行任何操作,是嗎? 直到解釋器運行時才建立; 它不對編譯器開放。

盡管其他答案是正確的,但我也值得一提的是在將-n-p切換到Perl時使用BEGINEND塊。

來自http://perldoc.perl.org/perlmod.html

當使用-n和-p切換到Perl時,BEGIN和END就像在awk中一樣工作,這是簡並的情況。

對於那些不熟悉-n開關的人,它告訴Perl用以下命令包裝程序:

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

如果您對有關Perl開關的更多特定信息感興趣,請http://perldoc.perl.org/perlrun.html#Command-Switches

作為演示使用-n開關使用BEGIN的示例,此Perl單行代碼列舉了ls命令的行:

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

在這種情況下, BEGIN塊用於在處理ls的行之前通過將其設置為1來初始化變量$i 此示例將輸出類似:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM