中文版 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 |