中文版 Perl CGI 程式寫作常問問題集(五) - 中國WEB開發者網絡 (http://www.webasp.net) -- 技術教程 (http://www.webasp.net/article/) --- 中文版 Perl CGI 程式寫作常問問題集(五) (http://www.webasp.net/article/8/7743.htm) |
| -- 作者:未知 -- 發佈日期: 2003-09-13 |
| Q4.6: 要如何用一個 Perl 的取代指令將所有 HTML 標籤從一份文件中刪除? 以下這個簡單的 regular expression 可用來去除 HTML 標籤*: 【譯者】 1. 要讓這個 regular expression 跨行執行,您必須先將您的 script 由預設的按行執行模式 (line mode) 改為按段執行模 式 (paragraph mode)。您可以在指令列以: perl -00 -we '...' 的方式;或是在 script 中以: #!/usr/bin/perl -00 或 $/ = ""; 的方式來設定按段執行模式。 2. 除非您需要對欲刪除的 HTML 標籤中的內容做進一步的處理或 利用,否則本例中最外圍的一對括弧可去掉。 $line =~ s/<(([^ >]|\n)*)>//g; 詳細的相關資料,請看 Tom [Christiansen] 的 striphtml 程式 (<ftp://perl.com/perl/scripts/striphtml>,這個程式同時也收錄在他的 tour of perl5 regexps (<http://perl.com/perl/all_about/regexps.html> 講義中。 ---------------------------------------------------------------------- Q4.7: 要如何知道是誰/哪台機器/哪個瀏覽器執行了我的程式? 您可以從 HTTP_USER_AGENT 這個環境變數得知使用者所用的瀏覽器。 【摘自 WWW FAQ】 您的 CGI script 可以利用五個重要的環境變數來幫忙辨識使用者的身份。 * HTTP_FROM 這個環境變數理論上應設為使用者的email地址。但是許多瀏覽 器完全不加以設定【即不支援】,而大部份支援這個變數的瀏覽器又讓使 用者自由設定這個值。因此,建議讀者頂多拿它來做為 email form 中回 信地址的預設值。 * REMOTE_USER 這個變數唯有當 script 在安全認證的保護下執行時才會被 設定。從 AUTH_TYPE 這個變數可以知道所用的認證方法是屬於哪一個類 型。REMOTE_USER 則會含有正接受認證的使用者的名字。要注意的是, REMOTE_USER 只有在使用安全認證的時候才會被設定,而且不是所有的 servers 都支援。在 NCSA server 底下,如果認證所使用的傳輸方式沒 有列入 access.conf 檔中(也就是說,應使用 <Limit GET POST>,而不是 僅僅用預設的 <Limit GET>),認證可能會出人意外地失敗。 * REMOTE_IDENT 如果 server 能連接上客戶端的 IDENT server,它會將這 個變數設成遠方使用者的身份。但由於向IDENT server 查詢的動作太花 時間,大部份的 servers 都把這項功能關掉。更何況,客戶端的機器是 否會回應查詢,又是否會誠實以對,都是無法確定的。 * REMOTE_HOST * 這個變數的設定值並不包括遠端使用者的真實身份,但是會提供使用者正 用來連線的機器名稱,只要 server 能找得出來。由於我們無法確切得知 使用者的真實身份【請看前一個環境變數的說明】,有的時候使用可確認 的位址來替代,不失為一個可行的變通方法。在 server 查不到遠端的機 器名稱,或者是為增加 server 的處理速度而將這個查詢功能關掉的情況 下,這個變數是空的;請看底下 REMOTE_ADDRESS 一項的說明。還有,別 忘了您可能會發現所有使用同一個 proxy (代理人) server 的使用者的 機器名都變成了那台 proxy server 的名字。 * REMOTE_ADDR 這個變數的設定值並不包括遠端使用者的真實身份,但是會 提供使用者正用來連線的機器的資料。REMOTE_ADDR 會包含客戶端的 IP 位址,以用點隔開的十進位數字的形式來表示。由於我們無法確切得知使 用者的真實身份 [請看前一個環境變數的說明],有的時候使用可確認的 位址來替代,不失為一個可行的變通方法。和前一項 REMOTE_HOST 不同 的是,這個變數一定會被設定。還有,別忘了您可能會發現所有使用同一 個 proxy (代理人) server 的使用者的機器位址都變成了那台 proxy server 的位址。 【摘錄自 WWW FAQ 部份完】 ---------------------------------------------------------------------- Q4.8: 人家看得到我的 Perl CGI 程式嗎?如果是這樣的話,那不就讓他們知道 我的程式是怎麼運作的了。這是個安全漏洞嗎?我要怎麼把它隱藏起來? 如果您將您的 server 設成對所有在一個特定目錄(如 cgi-bin)下的檔案,或 者是具有某些副檔名(如 ``.pl''、``.tcl''、``.sh'')的檔案一律都以 CGI 程式看待,那麼 server 只會執行這些程式。至於使用者是無法看到 script 本身的內容的。 但是如果您允許人們看您的 script (譬如把它放到 HTML 文件的根目錄下), 那麼只要是這個程式沒有安全上的漏洞,這並不能算是安全問題。如果這個程 式真的有安全上的破綻而您又允許使用者看這個程式,那麼他們便有機可乘, 進而利用這個弱點。 【譯者】上面這段原文作者是就遠方的客戶端的使用者而言。和這個 主題相關的一 個常問問題是: Q: 我的 Perl CGI scripts 必須將權限設為全世界可讀。可是這樣一 來,和我同 機器有帳戶的人,只要知道我的程式名稱,就可以瀏覽我 的 Perl 程式的內容;尤 其當其中牽涉到密碼的問題時。 A: 至少有兩個解決方法,一個簡單,一個複雜: 簡單的方法是,請您的系統管理者(如果不是您自己的話),將您的 CGI scripts 及密碼檔(如果您選擇將密碼存放在另一個檔案中的 話)的所有者設成 Web server 跑的使用者(最常見的是使用者 nobody ;使用群 nogroup 或 nobody), 然後將 CGI scripts 的使 用權限設定成 550 (-r-xr-x---),密碼檔的權限設成 440 (-r--r-----)。如此一來,一方面您的程式得以執行,而且其他同機 器上的 使用者也沒有辦法偷看到您的程式和密碼。 比較複雜的解決方法是先挑個難破的密碼將整個程式加密起來,然後 再使用 Filter::decrypt 這個模組在臨執行前將其解開,在此不多 說。有興趣的讀者請看 Filter::decrypt 的使用說明;此外新的 perl FAQ 第三部分中這一段:``How can I hide the source for my Perl program?'' ,大家也可參考。 ---------------------------------------------------------------------- Q4.9: 我需要將整個 Perl library 都複製到我的 htdocs 目錄底下嗎? 不需要。您的 CGI scripts 可以使用 server 和文件根目錄之外的任何檔案, 除非 server 是在一個 chroot 的環境下執行。 ---------------------------------------------------------------------- Q4.10: 我為什麼不該叫使用者輸入他們的密碼或身份證字號或信用卡號碼?有 一個 TYPE="password" 不是就是拿來做這個的嗎? No! form 的介面中有一個 ``password'' 的欄位,但是您不應該拿它來處理任 何機密性的資料。不該這麼做的原因是因為所有的 form 資料(包括 ``password'' 欄) 都是以純文字形式,而非以加密形式由瀏覽器送至 server。 如果您想要安全地傳送資料,那麼您需要使用具有安全功能的 server,例如 Netscape 的 Commerce Server (<http://home.netscape.com/comprod/products/iapps/platform.html>*。 【譯者】Apache SSL ,例如 Stronghold 版 (<http://stronghold.c2.net>,同樣具有這個功能。 ---------------------------------------------------------------------- Q4.11: 我要如何產生專門替 Netscape 設計的網頁,以別於世上其他的瀏覽 器? 您可以透過 HTTP_USER_AGENT 這個環境變數在您的 CGI script 中得知是否 Netscape 正在執行您的 script。以下為一例: $browser = $ENV{'HTTP_USER_AGENT'}; if ($browser =~ /Mozilla/) { # # Netscape # } else { # # Non Netscape # } ---------------------------------------------------------------------- Q4.12: 為什麼我的 system() 所產生的資料輸出順序不對? 這是由於標準輸出的產生方式通常是先累積相當的資料再輸出(buffered)。要 讓輸出的資料以正確的順序顯示,您必須藉由 $| 這個變數的設定將 buffering 的特性關掉。 ---------------------------------------------------------------------- Q4.13: 我聽說 Netscape 會支援 Java*。這是不是說我現在得棄 Perl,改 Java 了?是不是該這麼做? 【譯者】原 FAQ 已有相當一段時間未更新。這句話現在應該改作 「Netscape 和 IE 兩大瀏覽器都已支援 Java」。 不、不、不。Java 和 CGI 的概念完全不同。CGI 是在 server 端執行,而 Java則是在 client 端執行。有些東西(如動畫)可藉由使用 Java 而得到較好 的效果。但您可繼續使用 perl 來發展 server 端的應用程式。 如果您需要有關 Java 進一步的資料,底下列了幾個文件您可以去看看*: * 升陽公司的 Java 文件 (<http://java.sun.com> * Tom C.所寫的 Java uber Alles(Java 的種種) * Java, the Illusion(Java 幻像) 【譯者】後面這兩篇文章對 Java 及這個熱潮作了很嚴厲的批判。本 FAQ 作者 Tom C. 的 Java uber Alles 中的論點主要著重於技術層 面。Tom 對 Java 的態度或許代表了不少 Perl 陣營人仕的心聲。 ---------------------------------------------------------------------- Q4.14: 我要如何讀取環境變數?為什麼它們有時候會不一樣? 您可以透過 %ENV 這個關連陣列來讀取環境變數。以下這個簡單的 script 會把 所有的環境變數印出來(排好順序): #!/usr/local/bin/perl -w print "Content-type: text/plain", "\n\n"; foreach $key (sort keys %ENV) { print $key, " = ", $ENV{$key}, "\n"; } exit 0; 很不幸的,有些環境變數會被某些瀏覽器忽略掉。譬如,有些瀏覽器就不設定 HTTP_REFERER。 ---------------------------------------------------------------------- Q4.15: 為什麼我輸出的資料被攪亂了(如 ``b < a'' 會被破壞掉)? 如果您送的 MIME 類型是 HTML 的話,您必須「跳脫」 (escape) 某些符號, 如 ``<''、``&anp;'',及 ``>'',否則瀏覽器會以為它是 HTML 【標籤】。 您必須使用以下格式來跳脫特殊字元: ASCII 代碼; 您可以在指令列執行這個簡單的 script,便可得到非字母數字性字元 (non alpha-numeric characters) 所對應的 ASCII 碼: #!/usr/local/bin/perl -w print '請輸入字串: '; chop($string = <STDIN>); $string =~ s/([^\w\s])/sprintf("%d;", ord($1))/ge; print '跳脫過的字串是: ', "$string\n"; exit 0; ---------------------------------------------------------------------- Q4.16: 為什麼我的Perl CGI 程式可以由指令列,卻無法從瀏覽器去執行? 最可能的原因是權限的問題。別忘了,您的 server 可能是以 ``nobody''、 ``www'',或其他權限很低的帳戶身份來執行的。因此,除非它有足夠的權限, 否則是無法執行您的 script 的。 ---------------------------------------------------------------------- Q4.17: 為什麼我的 Perl CGI 程式能跑,但是不會把資料寫到檔案中? 這又是權限在作怪!server 除非有足夠的權限否則是無法將資料寫進某目錄下 的某檔案裡去的。 您應該養成習慣檢查 open 指令遞回的錯誤狀態 (error status): print "Content-type: text/plain\n\n"; . . . open(FILE, ">/some/dir/some.file") || print '無法寫進檔案: ', "$!\n"; . . . ---------------------------------------------------------------------- Q4.18: 要如何做一個會維繫狀態,或允許【同一使用者】多次連線的 form? 您可以用 CGI::MiniSvr 這個 module 來維持【記住】幾次不同的連線之間的狀 態資料。 或者,您可以制做一系列的動態文件,在彼此之間相互傳遞一個期間代碼 (session ID),此代碼可以以詢問 (query)、額外路徑,或隱藏式欄位等形式存 在*。 【譯者】CGI.pm 會替您把這部份(維持狀態)做好 (用上述的原理 ),故使用 CGI.pm 可自動享受這項功能,不需要自己去做。這又多 了一個該使用 CGI.pm 的理由。 ---------------------------------------------------------------------- Q4.19: 如果不從瀏覽器去執行我的 CGI 程式,要如何替它除錯? CGI 程式不容易除錯。您可以藉著手動設定環境變數來模擬 server: setenv HTTP_USER_AGENT "Mozilla/2.0b6" (csh) 或 export HTTP_USER_AGENT = "Mozilla/2.0b6" (ksh, bash) 要模擬 POST 請求,您可以把資料先放進一個檔案裡,然後把它 pipe 到您的程 式去: cat data.file | some_program.pl 或者您可以用 CGI.pm 來幫您除錯。假設您有一個像下面這樣的 script,它會 把所有您傳給它的索引/設定值對應資料 (key/value pairs) 都列印出來。 #!/usr/local/bin/perl -w use CGI; $cgi = new CGI; print $cgi->header; print $cgi->start_html("Simple CGI.pm Program"); print "<H1>Simple CGI.pm Program</H1>\n"; print "<HR >"; print '以下所列的是您傳送的設定值:'; print $cgi->dump; exit 0; 這個 script 不會在乎您是透過 GET、POST,或 ISINDEX 請求,或者是由指令 列、標準輸入,或文字檔將資料傳送給它。為了方便除錯,我們就直接從指令 列傳一些資料給它吧: % simple.cgi first=shishir last=gundavaram document='CGI\ FAQ' 或 % simple.cgi "first=shishir&last=gundavaram&document='CGI\ FAQ'" 在第二個例子中,整個字串周圍必須加引號("),否則 shell 看到 ``&'' 這個 符號會誤解。好,接下來是從標準輸入來除錯的方法: % simple.cgi (waiting for standard input) first=shishir last=gundavaram document=CGI\ FAQ ^D 當然,您也可以先用一個檔案來儲存資料,然後再做輸入轉向,像這樣: % simple.cgi < form.data 您也可以用 CGI Lint (即將出版)。它能達到相同的功效。另外,它也能幫忙 檢查有無安全問題,不當使用 open(),以及不正確的 HTTP 標頭等。 ---------------------------------------------------------------------- Q4.20: 如果不靠<FORM>標籤,要如何叫出 Perl CGI 程式? 您可以直接去打開該 CGI 程式的 URL: http://some.machine/cgi-bin/your_program.pl 您也可以在文件中使用連結的方式,例如: <A HREF="http://some.machine/cgi-bin/your_program.pl> 要試試我的CGI程式請在這裡點一下</A> ---------------------------------------------------------------------- Q4.21: 要如何避免旁人不先填欄位就執行我的 form?他們為什麼一直不斷這麼 做? 這些人欄位完全空白就去執行 form 是因為他們把這個 form 的 URL 儲存起來 【儲存到書籤裡面】的關係。當他們下次叫出這個 form 的時候,這個請求就會 變成是一個空的 GET (而非 POST 或填有資料的 GET)。 您可以先檢查所有欄位中的資料,如果其中有欄位留白的話,您可以送回一個 ``No Content'' 的狀態屬性*。以下是一例(假設關連陣列 %form 中含有您 form 的資料): 【譯者】狀態碼 204 的屬性已由 HTTP 0.9 (<http://www.w3.org/pub/WWW/Protocols/HTTP/HTTP2.html> 的 ``No Response'' 變為 HTTP 1.0 (<ftp://ds.internic.net/rfc/rfc1945.txt> 和 HTTP 1.1 (<ftp://ds.internic.net/rfc/rfc2068.txt> 中的 ``No Content'' 了。 $error = 0; foreach $value (values %form) { $value =~ s/\s//g; $error = 1 unless $value; } if ($error) { print "Content-type: text/plain\n"; print "Status: 204 No Content\n\n"; print '除非您的瀏覽器不支援狀態碼 204 ,否則您不該看到這部份', "\n"; } else { # # Process Data Here # } ---------------------------------------------------------------------- Q4.22: 那些server 回應碼 (server response codes) (<http://www.w3.org/hypertext/WWW/Protocols/HTTP/HTRESP.html>是幹什麼 用的?有什麼意義? CGI 程式可以傳送 server 然後 server 會把它轉送給瀏覽器。例如:假設您 想送``No Content'' (意思是告訴瀏覽器不要再重新下載該網頁),那麼您得 送一個 204 的回應碼(見上例)。 ---------------------------------------------------------------------- Q4.23: 為什麼 print "Location: http://host/page.html\n 不 work?又為 什麼它只 work 一次,但隨後的轉向就都弄錯了呢? CGI 程式只能送一個 Location 標頭。還有,如果您要 server 做轉向的動作 您就不該送 MIME 類別。譬如,以下的例子是錯誤的示範,儘管在有些 servers 上行的通: #!/usr/local/bin/perl -w . . . print "Content-type: text/plain\n" print "Location: http://some.machine/some.doc\n\n"; ---------------------------------------------------------------------- Q4.24: 要如何讓 server 在每個 HTML 網頁的底部都自動加上一個:「最近更 新日期: ...」的告示?或者,是不是只有 SSI 的網頁才能這麼做?CGI 程式 的日期要如何取得? 如果您是透過 CGI 以動態方式來產生您的文件,那麼要插入一個時間標記非常 簡單。以下是一例(僅適用於 Perl 5): $last_updated = localtime; print '最近更新日期: ',"$last_updated\n"; 或者是: require "ctime.pl"; $last_updated = &ctime(time); print '最近更新日期: ', "$last_updated\n"; 甚至像這樣: chop($date = `/usr/local/bin/date`); print '最近更新日期: ', "$last_updated\n"; 您可以用 SSI 來達到這個效果,像這樣: <--#echo var="LAST_MODIFIED"--> ---------------------------------------------------------------------- Q4.25: 什麼樣的場合下以 Perl 寫 CGI 程式會顯得太小題大作,因為用 shell 就可以做到?而什麼樣的場合對 Perl 來說又過於困難?用 C++ 做這類的事不 是好得多嗎?那用 C 呢? 每一個語言都有其長處和短處。相信這句話您聽過很多次了。所以一切全看您要 做的是什麼而定。如果您預期正準備寫的 CGI 程式每個鐘頭會有幾千幾萬人次 連去使用,那麼您應該選用 C 或 C++來寫。如果您求快的話(指發展所花費的 時間而言),那麼 Perl 是正確的選擇! 一般說來,您應避免用 shell 來做任何形式的 CGI 程式設計,因為 shell 在 先天上容易產生安全問題。 |
| webasp.net |