perlfaq3 - perl常問問題集,第三篇

目錄


篇名

perlfaq3 -程式設計工具(原文版 Revision: 1.22, Date: 1997/04/24 22:43:42. 中譯版 $Revision: 1.4 $, $Date: 1997/07/12 20:03:10 $)


概述

這個部份回答了有關程式設計師的工具與程式設計方面的協助等相關問題。


我如何作 (任何事)?

你到 CPAN(見 perlfaq2)找過了嗎?也許別人已經寫了某個模組可以解決你的 問題。你查過相關的說明文件了嗎 (man pages)?以下是一份概要的索引:

物件 (Objects)                  perlref, perlmod, perlobj, perltie
資料結構 (Data Structures)      perlref, perllol, perldsc
模組 (Modules)                  perlmod, perlmodlib, perlsub
正規表示法 (Regexps)            perlre, perlfunc, perlop
升級至 Perl5 (Moving to perl5)  perltrap, perl
與 C連結 (Linking w/C)         perlxstut, perlxs, perlcall, perlguts, perlembed
雜項 (Various)                  http://www.perl.com/CPAN/doc/FMTEYEWTK/index.html
                                        (不是說明文件,但還是很有用)

perltoc裡有一份粗略的 perl 說明文件組的目錄。


如何以互動的方式使用 Perl?

典型的作法是使用 perldebug(1)說明文件裡提到的 Perl 除蟲器,在一個「空的」(譯者:即不存在的)程式上執行,像這樣:

    perl -de 42

接下來所打入的任意合法 Perl程式碼皆會立刻被評估。同時,你可以檢查符號表 (symbol table)、取得堆疊的記錄 (stack backtraces)、檢視變數值、設定阻斷點 (set breakpoints) 以及其他符號式除蟲器 (symbolic debuggers) 所能作的動作。


有 Perl shell嗎?

基本上來說,沒有。Shell.pm模組 (是 perl 標準套件之一)只是叫 perl 將非 Perl語言的命令當作 shell的命令來試著執行看看罷了。perl原始碼套件中的 perlsh,功能簡易,也很無趣,不過仍可能是你所要的。


如何替我的 Perl程式除蟲?

你用過 -w嗎?

你試過 use strict嗎?

你是否檢查過每個系統呼叫 (system call)所傳回的值?

讀了 perltrap說明文件嗎?

你試過 perldebug裡所提到的 Perl除蟲器嗎?


如何檢測 (profile)我的 perl程式?

你該自 CPAN抓取 Devel::DProf 模組,並且使用 perl 標準套件所附的 Benchmark.pm。 Benchmark.pm讓你測量程式碼的某部份在執行上所花的時間,而 Devel::DProf則詳細地替你分析哪一部份的程式用掉多少時間。


如何替我的 Perl程式作交叉參考 (cross-reference)?

隨著新發行的 alpha版 Perl編譯器(它不在一般標準套件裡)而來的 B::Xref模組可 以替你的 Perl程式製作 cross-reference報告。用法是:

    perl -MO=Xref[,OPTIONS] foo.pl


有 Perl專用的美化列印程式 (pretty-printer)嗎?

C有 indent(1)可以將原始碼格式美化,但 Perl並沒有能做得像它那麼好的東西。掃瞄器 (scanner) 和分析器 (parser) 間複雜的反饋 (feedback)(把 vgrind 和 emacs等程式搞混的就是這反饋)使得撰寫一個獨立的 Perl 分析器成了一項艱巨的挑戰。

當然,若你直接照 perlstyle裡面的指示寫程式,就根本沒有必要重新安排格式。

你所用的編輯器可以並也應能幫你把原始碼的格式弄漂亮些。像 emacs的 perl-mode就能幫你把大部分 (但非全部)的程式碼排列得漂亮些,而其它普通的編輯器也能提供一定程度的協助。

如果你試著使用 vgrind程式從雷射印表機印出漂亮的原始碼,可以參考: http://www.perl.com/CPAN/doc/misc/tips/working.vgrind.entry ,但是碰到複雜的程式碼可能就不能全然令人滿意了。


有 Perl的 ctags嗎?

有個簡單的在 http://www.perl.com/CPAN/authors/id/TOMC/scripts/ptags.gz 也許符合你的需要。


哪裡有 vi用的 Perl巨集?

ftp://ftp.perl.com/pub/vi/toms.exrc有完整的 Tom Christiansen之 vi設定檔, 它是給 vi模擬器用的標準測試檔 (standard benchmark file)。它與 nvi配合得最好,巧的是,這個出自 Berkeley的編輯器也可以內嵌一個 Perl直譯器 --參看 http://www.perl.com/CPAN/src/misc。


給 emacs用的 perl模式又要去哪抓呢?

從大約 Emacs 19.22版 (version 19 patchlevel 22)起,已內含了 perl-mode.el及 perl 除蟲器的支援。它們應該會和標準的 Emacs 19版一起出貨。

在 perl原始碼的目錄下,你會找到一個叫作 ``emacs'' 的目錄,裡面包括一個 cperl-mode 可以把程式中的關鍵字上色、提供內文相關的協助以及其它方便的功能。

注意:``main'foo''(其中的單引號)會讓 emacs的 perl-mode生病,並且會弄亂內 縮 (indentation) 與精華 (hilighting)。不過你本來就該用 ``main::foo''的 (譯者按: main'foo 是表示模組或 package的舊式寫法;新式的 [perl5的]寫法是 main::foo)。


如何在 Perl裡使用 curses?

CPAN裡的 Curses模組提供了一個通往 curses 程式庫的動態載入物件模組介面。


X或 Tk如何與 Perl配合呢?

Tk這個完全以 Perl 為基礎,物件導向化的介面,讓你不用學 Tcl也可以使用 Tk工具組。Sx則是 Athena Widget set專用的介面。兩者都可在 CPAN取得。


如何不靠 CGI或 Tk之助作出簡單的目錄(選單)?

http://www.perl.com/CPAN/authors/id/SKUNZ/perlmenu.v4.0.tar.gz 是個以 curses為基礎的模組,可以達成你的要求。


我可以動態地將 C常式載入 Perl嗎?

若你的系統架構有支援的話,標準 perl 套件便應該有此功能(介由 DynaLoader 這個模組)。詳情請參看 perlxstut


什麼是 undump?

看下個問題。


如何讓我的 Perl程式跑得更快些?

最好是能設計一個較好的演算法 (algorithm),這通常會讓程式有大不相同的表現。 駱駝書第八章裡有些你或許想知道的增進效率小技巧。

其它方法包括自動載入較少使用的 Perl 程式碼。請參看標準 perl 套件中的 AutoSplit及 AutoLoader模組的用法。或當你能斷定程式執行效率的瓶頸在何處時,用 C來寫那個部份,就像用組合語言來撰寫 C程式的瓶頸部份一樣。與此法相近的是使用以 C撰寫瓶 頸部份的模組 (例如 CPAN中的 PDL 模組)。

在某些情況下,使用後端的編譯器把程式編譯成位元碼 (byte code)(可節省編譯時間) 或是將 perl程式轉編譯為 C程式的作法值得一試;這些作法絕對會節省編譯的時間並且有時能省一些[但不多]執行時間 。請參考“編譯你的 Perl程式”這個問題的答案。

如果你目前是將你的 perl直譯器動態連結到 libc.so的話,重新作一份靜態連結到 libc.a的 perl直譯器可以提高 10-25%的執行效能。雖然這會使你的 perl直譯器變得更胖,但你的 Perl程式 (及程式設計者) 或許會因此而感謝你。詳情請參考 perl標準套件原始碼版本中的 INSTALL 檔案。

一些未經證實的報告中宣稱有些使用 sfio的 Perl直譯器表現得比沒有用 sfio的還好 (針對於 IO頻繁的應用程式)。想試試看?參考 perl套件原始程式版中的 INSTALL 檔案,尤其是 ``Selecting File IO mechanisms''這一段。

使用 undump程式把編譯後的檔案格式存到硬碟裡以加快執行的速度已經是老掉牙的手法了。它已不再是個可行的方法,因為這方法只有幾種平台能用,況且它終究不是個治本之 道。


如何讓我的 Perl程式吃少一點的記憶體?

當問題變成時間與空間的交易時, Perl 幾乎總是用記憶體來幫忙解決問題。 Perl中的純量 (Scalar) 耗掉的記憶體比 C中的字串形態還多,陣列又更多, 更別談雜湊陣列了 (Hashes)。關於這一點,我們當然還有很多工作得作,近來發佈的版本,已開始針對這些問題做改進了。例如, 5.004 版中, 重複的雜湊陣列索引值 (duplicate hash keys) 由使用它的雜湊陣列共用,這樣就不用再重新定份位置給它了。

在某些情況下,使用 substr()vec()來模擬陣列有很大的好處。例如,一個有上千 個布林代數值的陣列將佔用至少 20,000位元組的空間,但是它可以被轉變為一個 125位元組的位元向量 (bit vector)以節省相當可觀的記憶體。標準套件中的 Tie::SubstrHash模組也能夠幫助特定形態的資料結構節省些記憶體。若你正在和一些特殊的資料結構奮戰 (例如,矩陣),用 C寫的模組所耗掉的記憶體可能低於同功能並用 Perl寫的模組。

另一件值得一試的是,查一下你的 Perl是以系統內的 malloc 還是 Perl內含的 malloc 編譯起來的。不論是哪個,試著換成另一個,再看看這是否造成任何差別。關於 malloc的資訊可在 perl標準套件原始碼版中的 INSTALL 檔案找到。鍵入 perl -V:usemymalloc就可以知道你是否在使用 perl的 malloc。


把指標傳回到區域資料是不安全的做法嗎?

不,Perl的資源回收 (garbage collection)系統會解決此問題。

    sub makeone {
        my @a = ( 1 .. 10 );
        return \@a;
    }

    for $i ( 1 .. 10 ) {
        push @many, makeone();
    }

    print $many[4][5], "\n";

    print "@many\n";


我如何釋放一個陣列或雜湊陣列以縮小我的程式尺寸?

你無法這麼作。系統配置給程式的記憶體是覆水難收。這也是為何執行很長一段時間的 程式有時會重新執行 (re-exec)它們自己的原因。

然而,在使用你的變數時,明智地用 my()來定義執行範圍,可讓 Perl在脫離該範圍後 將它們所佔的空間釋放給其它部份的程式。 (註:my()的變數也比全域變數執行起來快 10%。)當然,一個全域變數永遠沒有超出範圍的時候,所以你無法將它佔用的空間自動重新分配,不過,把它 undef() 或/和 delete()會有相同的效果。總之,在 Perl裡,你並不能/應該去擔心太多有關記憶體定址與解除這件事,而我們連添加這項功能(資料形態的預先定址),目前都已在進行中。


如何讓我的 CGI腳本 (script)執行起來更有效率?

除了使一般 Perl程式加快或縮小的平常手段外,一個 CGI 程式還有其他的顧慮。也許它每秒會被執行好幾次。每次它執行時,重新編譯所花的時間、加上定址所需的 1 MB以上的系統記憶體,就是一個大殺手。光是編譯成 C 是沒啥幫助的 ,因為瓶頸在於整個程序開始時所負擔的包袱 (start-up overhead) 。

最起碼有兩種較流行的方法可以避免這些包袱。一種解法是將 mod_perl 或是 mod_fastcgi其中一個模組加在你所執行的 Apache HTTP server (可從 http://www.apache.org/取得)。有了 mod_perl 和 Apache::*模組 (從 CPAN取得),httpd執行時會帶起一個內 嵌的 Perl直譯器,而它會預先編譯你的程式,並在不產生其它子程序的情況下用同一個定址空間來執行。Apache 擴充模組亦給 Perl一個連通 server API 的管道,所以用 Perl寫的模組可以做到任何 C寫的模組所具備的功能。而有了 FCGI模組 (自 CPAN取得),你以 sfio (參看 perl標準套件原始碼版本中的 INSTALL檔案) 和 mod_fastcgi (從 http://www.fastcgi.com/取得)模組編譯成的 Perl 直譯器將使你的每個 perl程式變成一個固定的 CGI 背景程序 (daemon process)。

這些方法對你的系統與你撰寫 CGI程式的方法都有超乎想像之外的影響,所以請小心地 探索它們。


如何隱藏 Perl程式的原始碼?

刪除它。 :-) 說真的,有一些具有不同“安全”等級的方法(大部分都不能令人滿意)。

然而,首先,你 不能拿走讀取權,不然你的程式怎麼被解譯或是編譯呢? (不過那也並不表示一個 CGI程式的原始碼可以被使用者讀取。)所以你得讓檔案權限停留在 0755這個友善的階段。

有些人認為這是個安全上的漏洞。不過若你的程式作的是不安全的事情,光仰賴別人 看不見這些漏洞、不知從何下手,那麼它依然是不安全的。其實對有些人來說他們並 不需要看見程式原始碼便可能判定並揭露這些不安全的部份。透過隱瞞達到的安全, 就是不修正臭蟲反而隱藏它們,實際上是沒有安全性可言的。

你可以試著透過原始碼過濾模組 (CPAN中的 Filter::*)來替原始碼加密。但高手也許有 辦法將其解密還原。你也可以用下面提到的 byte code 編譯器與直譯器,但高手也有可能反解譯它。你可以試試後面提到的原生碼編譯器 (native-code compiler),但高手也有可 能反組譯它。這些手段都需要不同難度的技巧才能讓別人拿到你的原始碼,但沒有一種能 夠很確定地隱藏它。(這對每種語言來說都為真,不是只有 Perl)

如果你所擔心的是別人自你的程式碼中獲利,那麼一紙權限執照是能提供你法律上安全的唯一途徑。註冊你的軟體並且寫份權限說明,再加上一些具威脅性的句子像“這是 XYZ公司未出版的專有軟體。你能擷取它並不代表你具有使用的權限...”之類云云。當然,我們不是律師,所以若你想要你的執照中每一句話在法庭上都站得住腳,就去見個律師吧。


如何把我的 Perl程式碼編譯成 byte code或 C?

Malcolm Beattie已經寫了一個多功能的後端編譯器,可以從 CPAN取得,它就能做到這兩項功能。1997 年二月是 alpha測試版的最後幾個階段,這代表著若你是個程式設計 員而非尋找萬靈解藥的人,那麼參與其測試就會充滿趣味。

了解光是編譯成 C 其本身或在本質上並不能保證它就會跑得快更多。那是因為除了 在運氣好的狀況中有一堆可以衍生成出來的原生形態外,平時的 Perl 執行系統環境依然 存在因此依然會花差不多長的執行時間與佔用差不多大小的記憶空間。大多數程式能省下 來的不過是編譯時間,這使執行速度頂多快 10-30%。有些罕見的程式能真正從中受利 (例如增快好幾倍),但這還得配合原始碼的微調。

Malcolm 將會主導 Perl 5.005 版的發展並試著將其編譯器與多執行緒部份的工作融合進主要的發行版本裡。

你或許會驚訝地發現,現行版本的編譯器做出來的執行檔大小跟你的 Perl直譯器一樣大,有時更大些。那是因為依照現在的寫法,所有的程式皆轉成一個被 eval()的大敘述。只要建造一個動態連結的 libperl.so程式庫,並將之連結起來,你就可以戲劇性地減少這 種浪費。參看 perl原始碼套件中的 INSTALL pod檔案以獲得更詳盡的訊息。如果你用這方法連結你主要的 perl執行檔,就能使它變得很渺小。舉例來說,在作者之一的系 統裡, /usr/bin/perl只有 11k“小”而已!


如何才能讓 '#!perl'在 [MS-DOS,NT,...]下作用?

OS/2下只要用:

    extproc perl -S -your_switches

當作 *.cmd檔案的第一行 (-S 是因 cmd.exe中其 `extproc'處理的臭蟲才要的)。DOS使用者應先製作一個相對的 batch 檔案然後將它以 ALTERNATIVE_SHEBANG 的方式寫成程式。(更多訊息在原始碼版本的 INSTALL檔案裡)

若安裝 Activeware版的 Win95/NT 專用 Perl,它會更動 Registry的內容,把 .pl 的擴充檔名與 perl直譯器結合。如果你安裝另一版本或是用 WinGCC建構你自己的 Win95/NT用 Perl,那你就得自己更動 Registry的內容了。

麥金塔的 perl程式將會有適當的創造者與形態 (Creator and Type),所以雙擊它們就會執行這些 perl 應用程式。

重要:不論你做什麼,請千萬不要因為覺得沮喪,就把 perl 直譯器丟到你的 cgi-bin目錄下,好讓你的 web 伺服器能執行你的程式。這是一個非常大的安全漏洞。花點時間想 想怎樣才是正確的做法吧。


我能利用命令列寫出有用的程式嗎?

可以。詳情請看 perlrun。以下有些範例 (假設用的是標準的 Unix shell引言規則)。

    #把第一欄和最後一欄相加
    perl -lane 'print $F[0] + $F[-1]'

    #辨別是否為文字檔
    perl -le 'for(@ARGV) {print if -f && -T _}' *

    #移除 C程式中的說明
    perl -0777 -pe 's{/\*.*?\*/}{}gs' foo.c

    #讓檔案年輕一個月,躲避追殺的魔鬼 (daemon)
    perl -e '$X=24*60*60; utime(time(),time() + 30 * $X,@ARGV)' *

    #找出第一個未用的 uid
    perl -le '$i++ while getpwuid($i); print $i'

    #顯示合理的使用說明路徑 (manpath)
    echo $PATH | perl -nl -072 -e '
        s![^/+]*$!man!&&-d&&!$s{$_}++&&push@m,$_;END{print"@m"}'

好吧,最後一個例子事實上是「perl程式困惑化」競賽 (Obfuscated Perl)的 參賽作品。 :-)


為何一行的 perl程式無法在我的 DOS/Mac/VMS系統上運作?

問題通常出在那些系統的命令解譯器對於參數的引用與 Unix shells 所作的解釋不同,而後者很不幸的是這些一行 perl 的生父。在某些系統,也許你得把單引號改成雙引號,但這卻是你萬萬 不可在 Unix或 Plan9系統上作的事。你也許還得把一個 %改成 %%。

例如說:

    # Unix
    perl -e 'print "Hello world\n"'

    # DOS,等。
    perl -e "print \"Hello world\n\""

    # Mac
    print "Hello world\n"
     (然後執行 "Myscript"或按 Shift-Command-R)

    # VMS
    perl -e "print ""Hello world\n"""

問題是,這些方法沒有一個是完全可靠的:它都得看命令解譯器的臉色。在 Unix中,前兩者通常可以用。在 DOS下,兩者可能都沒有用。若 4DOS是命令解譯器,下面此法可能比 較有希望:

  perl -e "print <Ctrl-x>"Hello world\n<Ctrl-x>""

在 Mac 下,端視你所用的環境為何。 MacPerl所附的 shell,或是 MPW, 其所支援的參數格式有不少都蠻像 Unix shells的,除了它自在地使用 Mac 的非 ASCII字元當成控制字元。

恐怕我得說這問題並沒有一般解。白話一點說,它真是一團亂。

[部份答案是由 Kenneth Albanowski 所提供的。]


我得去哪裡學 Perl的 CGI或是 Web程式設計呢?

就模組來說,去 CPAN抓 CGI 和 LWP 兩個模組。就書本來看,參考關於書那部份裡特別和 web 相關的問題。若有與 web相關的疑難雜症,像“為何我收到 500錯誤”或“它在命令列模式下跑得好好的,怎麼不能在瀏覽器下正常執行”時,請參看:

    The Idiot's Guide to Solving Perl/CGI Problems, by Tom Christiansen
        http://www.perl.com/perl/faq/idiots-guide.html

    Frequently Asked Questions about CGI Programming, by Nick Kew
        ftp://rtfm.mit.edu/pub/usenet/news.answers/www/cgi-faq
        http://www3.pair.com/webthing/docs/cgi/faqs/cgifaq.shtml

    Perl/CGI programming FAQ, by Shishir Gundavaram and Tom Christiansen
        http://www.perl.com/perl/faq/perl-cgi-faq.html

    The WWW Security FAQ, by Lincoln Stein
        http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html

    World Wide Web FAQ, by Thomas Boutell
        http://www.boutell.com/faq/

(譯者:上面第三份文件,Perl-CGI-FAQ的中譯版可在 http://2Ti.com/cgi-bin/2T/perl/perl-cgi-faq-chi/ 處取得。最後一份(WWW FAQ)的中譯版可自 http://www.acer.net/document/cwwwfaq/ 取得。)


在哪可以學到用 Perl作物件導向程式設計?

perltoot是個好開始,然後你可以再參考 perlobjperlbot。Perltoot直到 5.004版本才誕生,但你可以從 http://www.perl.com/CPAN/doc/FMTEYEWTK/下取得 (pod、html,或 postscript 格式)。


哪裡可以學到將 C與 Perl相連結? [h2xs, xsubpp]

若你要從 Perl程式呼叫 C,就自 perlxstut開始向 perlxsxsubpp ,及 perlguts前進。反之,則讀 perlembedperlcall ,及 perlguts 。別忘了 你可以從各模組的作者如何寫他們的模組及解決他們的問題中學到很多。


我已經讀了 perlembed, perlguts,等等,但我仍然無法將 perl嵌入我的 C程式,我做錯了什麼?

自 CPAN 下載 ExtUtils::Embed 套件,然後執行 `make test'。如果測試成功,就一遍又一遍地讀那些 pod 說明檔案。若它失敗了,參看 perlbug並送一份內有 make test TEST_VERBOSE=1perl -V輸出的報告。


當我試著執行我的程式時,我收到某項訊息。它代表什麼意思?

perldiag有一份完整的 perl錯誤與警告訊息列表,並附有說明文字。你也可以用 splain程式 (伴隨 perl而來)去解釋這些錯誤訊息:

    perl program 2>diag.out
    splain [-v] [-p] diag.out

更改你的程式讓它替你解釋這些訊息也可以:

    use diagnostics;

    use diagnostics -verbose;


什麼是 MakeMaker?

此模組 (亦為標準 perl 套件之一部份)設計的目的是要替一個模組從一 Makefile.PL 中自動撰寫出一個 Makefile。詳情請看 MakeMaker


作者與版權事宜

Copyright (c) 1997 Tom Christiansen and Nathan Torkington. All rights reserved.有關使用、(轉)發行事宜,詳見 perlfaq

譯者:陳彥銘

中譯版著作權所有:陳彥銘、蕭百齡及兩隻老虎工作室。本中譯版遵守並使用與 原文版相同的使用條款發行。