`
weiyinchao88
  • 浏览: 1180685 次
文章分类
社区版块
存档分类
最新评论

perl常见问题集(二)

 
阅读更多
如何得知使用者正在哪个作业系统下执行我的 perl 程式?
$^O 这个变数(若使用 English 模组就是 $OSTYPE)会指出你的 perl 解译器执 行档是替哪个作业系统、平台所建的。


--------------------------------------------------------------------------------

为什麽 exec() 不会传值回来?
因为这正是它所做的:它用另一个不同的程式来取代你当时所执行的。如果你的程 式需要继续跑下去(这可能正是你问此问题的原因吧?),改用 system() 。


--------------------------------------------------------------------------------

如何对 键盘/萤幕/滑鼠 做些花样?
连接/控制 键盘、萤幕和指标装置(「滑鼠」)的方法因作业系统的不同而有不 同;不妨试试下列模组:

键盘
Term::Cap perl 标准内建模组
Term::ReadKey CPAN
Term::ReadLine::Gnu CPAN
Term::ReadLine:erl CPAN
Term::Screen CPAN

萤幕
Term::Cap perl 标准内建模组
Curses CPAN
Term::ANSIColor CPAN

滑鼠
Tk CPAN


--------------------------------------------------------------------------------

如何向使用者询问密码?
(这个问题跟全球资讯网一点关系也没有。如果你要找的是跟 WWW 有关的,那就 看另一份常见问题集吧。)

【译注:中文版的 Perl CGI 程式设计常见问题集可以在下列网址中找到: http://www.math.ncu.edu.tw/~chenym/FAQ/Perl/perl-cgi-faq/
http://2tigers.net/perl/perl-cgi-faq-chi/ 】

在 crypt 里面有个范例。首先,将你的终端机设为「无回应」[no echo] 模式,然後就用平常的方法将密码读入。你可以用老式的 ioctl() 函数、 POSIX 终端机控制函数(参看 POSIX ,和 Camel 书第七章),或是呼叫 stty 程式,这些方法的可携性/移植性程度都不一样。

你也可以在大部份系统上使用 CPAN 里的 Term::ReadKey 模组,这个模组较易使 用而且理论上也较据可携性/移植性。


--------------------------------------------------------------------------------

如何对序列埠做读写动作?
这端看你在什麽作业系统上执行你的程式。以 Unix 来说,序列埠可以透过 /dev 目录下的档案来撷取; 而在其他系统上,设备的名称无疑地会不一样。以下是一些 在设备互动时可能遭遇的共同问题:

锁档 (lockfiles)
你的系统可能会使用锁档来控制多重读写的情况。确定你用的是正确的协定。因为 当多个程序同时对一个装置做读取时可能会发生意想不到的情况。
开档模式
如果你打算对一个装置同时做读与写的动作,你得将它开到更新的模式( 在 open 里有更详细的解说)。如果你不希望冒着阻挡其他程序读取 这个装置的风险,那就得用 sysopen() 和 Fcntl 模组(标准 perl 的一部分)内 的 O_RDWR|O_NDELAY|O_NOCTTY。在 sysopen 里有对此方法更 详尽的解说。
档案尾
有些装置会等着在每行结尾处看到一个 ``/r'',而非 ``/n''。在某些平台上的 perl, ``/r''和 ``/n'' 与它们平常(在 Unix 上)所指的 ASCII 值 ``/015'' 和 ``/012'' 有 所不同。你也许得直接给定数值,例如用八进位 (``/015'')、十六进位 (``0x0D''), 或指定控制字元 (``/cM'')。
print DEV "atv1/012"; # 对某些装置来说是错误的
print DEV "atv1/015"; # 对某些装置来说是对的

尽管对普通的文字档案,一个 ``/n'' 便可解决断行的问题,但目前在不同作业系统 间(Unix、DOS/Win 和 Macintosh),对於断行记号仍无统一标准,而只有用 ``/015/012'' 来当成 每行的结尾,然後再视需要去掉输出中不想要的部份。这 个做法尤其常用於 socket输出/输入 与自动洗清 (autoflushing),也是接下来 要讨论的主题。

洗清输出
如果你希望 print() 的时候每个字元都要送到你指定的装置去,那你应自动清洗 你的档案把手,旧方法是:
use FileHandle;
DEV->;autoflush(1);

比较新的方法是:

use IO::Handle;
DEV->;autoflush(1);

你可以用 select() 和 $| 变数来控制自动清洗的动作(参考 $| 和select ):

$oldh = select(DEV);
$| = 1;
select($oldh);

你也可能看到不使用额外的暂存变数的写法,例如:

select((select(DEV), $| = 1)[0]);

如同前一个项目所说的,这方法对 Unix 和 Macintosh 间的 socket 输出/入 没 用。在这种情况下,你得把你的行末字元写死在程式码内。

不挡式输入 (non-blocking input)
如果你正在做一个具阻挡性的 read() 或 sysread() 动作,则你需要安排一个闹 铃把手或提供一个逾时设定(参看 alarm)。如果你是用非阻挡式的 开档,那麽就要配合非阻挡性的读取,也就是说得用到4 个参数的 select() 来确 定此装置的 输出/入 是否已准备好了(参考 select )。

--------------------------------------------------------------------------------

如何逆解加密後的密码档案?
花大把大把的钱去买破解专用的硬体,这会让你成为焦点话题。

说正经的,如果是碰到 Unix 密码档的话就不行 - Unix 密码系统用的是单向的加 密函数。像 Crack 之类的程式可以暴力地(并聪明地)试着猜出密码,但无法 (也不能)保证速战速决。

如果你耽心的是使用者选取不良的密码,你应该在使用者换密码时主动审核(例如 说修改 passwd(1) 程式加入这个功能)。


--------------------------------------------------------------------------------

如何启动一个背景执行的程序?
你可以使用:

system("cmd &"

或是用 fork,像 fork 里写的(在 perlipc 里有更进一步的 范例)。如果你在 Unix 类的系统上的话,请注意以下几件事情:

STDIN, STDOUT 和 STDERR 是共享的
主程序和背景程序(即「子」程序)共用同一个 STDIN、STDOUT 和 STDERR 档案 把手。如果两个程序想同时去读、写同一个档案把手,就可能有怪事会发生。你也 许应该替子程序关闭或重新开启这些把手。你可以用开启一个管道 (pipe) 的方法 避免这些问题(参看 open)但是在某些系统上这样做会强迫子程序 必须比父程序早死。
讯号
SIGCHLD、可能还有 SIGPIPE 这两个讯号要抓到。当背景程序执行完成後就会送出 SIGCHLD 讯号。而当你写入一个子程序已经关闭的档案把手时就会收到 SIGPIPE 讯号(一个未抓住的 SIGPIPE 可能导致你的程式无声无息地死去)。用 system("cmd&" 的话不会有这样的问题。
僵 程序
你得做准备,在子程序结束时「收成」它:
$SIG{CHLD} = sub { wait };

在 Signals 有范例程式教你怎麽做。用 system("prog &" 的 话不会有僵 程序的问题。


--------------------------------------------------------------------------------

如何捕捉 控制字元/讯号?
你并不能真的 ``捕捉'' 一个控制字元。而是控制字元产生一个讯号让你捕捉。关於 讯号的资料可以在 Signals 以及 Camel 书第六章里找到。

要小心的是,大多 C 程式库无法重新进入 [re-entrant]。因此当你要尝试着在一 个处理器里做 print() 动作,而这个处理器是由另一个stdio 的动作所叫出来的 话,你的内部结构可能会处於失调状态,而程式可能会丢出记忆核心 (dump core)。 有的时候你可以用 syswrite() 取代 print() 以避免这个状况。

除非你极为小心,否则在一个讯号处理器中,唯一安全可做的是:设定一个变数後 离开。而在第一个情况下,你在设定变数的时候应确定 malloc() 不会被叫出来 (譬如,设定一个已经有值的变数)。

例如:

$Interrupted = 0; # 确定它有个值
$SIG{INT} = sub {
$Interrupted++;
syswrite(STDERR, "哇/n", 5);
}

然而,因为系统呼叫会自己重新启动,你将会发现如果你用的是「慢的」呼叫,像 、read()、connect() 或 wait(),那麽将它们停下的唯一办法是使 用 「跳远」的方式跳出来;也就是产生一个例外讯号。参看在 Signals 里对阻挡性 flock() 的逾时处理器的说明,或骆驼书第六 章。


--------------------------------------------------------------------------------

如何更动 Unix 系统上隐式密码档 (shadow password) 的内容?
如果你的 perl 安装正确的话,在 perlfunc 里描述的 getpw*() 函数应该就 能够读取隐式密码档了(只有读取权)。要更动该档案内容,做一个新的密码档 (这个档案的格式因系统而异,请看 passwd(5) )然後用 pwd_mkdb((参考 pwd_mkdb(5))来安装新的密码档。


--------------------------------------------------------------------------------

如何设定时间和日期?
假设你有足够的权限,你应该可以用 date(1) 程式来设定系统的时间与日期。 (但没有针对个别程序修改时间日期的方法)这机制在 Unix、MS-DOS、Windows 和 NT 下都能用;VMS 下则要用 set time 。

然而,如果你只是要更动你的时区,只消设定一个环境变数即可:

$ENV{TZ} = "MST7MDT"; # unix 下
$ENV{'SYS$TIMEZONE_DIFFERENTIAL'}="-5" # vms
system "trn comp.lang.perl";


--------------------------------------------------------------------------------

如何能够针对小於一秒的时间做 sleep() 或 alarm() 的动作呢?
如果你要比 sleep() 所提供的最小单位一秒更精细的话,最简单的方法就是用 select 里面写的 select() 函数。如果你的系统有 itimers 并支 援syscall(),你可以试试下面这个老范例 http://www.perl.com/CPAN/doc/misc/ancient/tutorial/eg/itimers.pl .


--------------------------------------------------------------------------------

如何测量小於一秒的时间?
一般来说,你可能做不到。 Time::HiRes 模组(CPAN 有)在某些系统上能达到此 功能。

总之,你可能做不到。但是如果你的 Perl 支援 syscall() 函数并支援类似 gettimeofday(2) 的系统呼叫,你也许可以这麽做:

require 'sys/syscall.ph';

$TIMEVAL_T = "LL";

$done = $start = pack($TIMEVAL_T, ());

syscall( &SYS_gettimeofday, $start, 0)) != -1
or die "gettimeofday: $!";

##########################
# 在这做你要做的事 #
##########################

syscall( &SYS_gettimeofday, $done, 0) != -1
or die "gettimeofday: $!";

@start = unpack($TIMEVAL_T, $start);
@done = unpack($TIMEVAL_T, $done);

# fix microseconds
for ($done[1], $start[1]) { $_ /= 1_000_000 }

$delta_time = sprintf "%.4f", ($done[0] + $done[1] )
-
($start[0] + $start[1] );


--------------------------------------------------------------------------------

如何做 atexit() 或 setjmp()/longjmp() 的动作?(例外处理)
第五版的 Perl 增加了 END 区块,可以用来模拟 atexit()的效果。当程式或执行 绪(thread) 终了时就会去呼叫该包装的 END 区块(参考 perlmod 文件)。但 是如果当程式被没有抓到的讯号终结了,END 区块就不会被呼叫到,所以当你用 END 时应再加上

use sigtrap qw(die normal-signals);

Perl 的例外处理机制就是它的 eval() 运算子。你可以把 eval() 当做 setjmp 而die()当做 longjmp 来使用。更详细的说明请参考 Signals 和 Camel书第六章里关於讯号的那段,尤其是描述有关 flock() 的逾时处理器那段。

如果你只对例外处理的部分有兴趣,试试 exceptions.pl 程式库(包含在标准 perl里)。

如果你要的是 atexit() 语法(以及 rmexit()),试试 CPAN 里的 AtExit 模组。


--------------------------------------------------------------------------------

为何我的 sockets 程式在 System V (Solaris) 系统下不能用?「不支援本协定」这个错误讯息又是什麽意思?
有些 Sys-V 根底的系统,特别像 Solaris 2.X,已重新将一些标准的 socket常数 定义过了。由於这些常数在各种架构下都是定值,所以在 perl程式码中常被人写 死在里面。处理此问题的适当方式 是用 ``use Socket'' 来取得正确的值。

须注意尽管 SunOS 和 Solaris 在二进位执行档上相容,这些值是相异的。自己去 想为什麽吧。


--------------------------------------------------------------------------------

如何从 Perl 里呼叫系统中独特的 C 函数?
通常是写个外部的模组来处理 - 参看「我要如何学到将 C 与 Perl 连结在一起? [h2xs, xsubpp]」 这问题的答案。然而,如果此函数是个系统呼叫,而你的系统 有支援 syscall(),那麽可以用 syscall 函数(说明在 perlfunc 里)。

切记先查查看你的 perl 版本中所附的模组以及 CPAN 里的模组,因为也许某人已 经写了个这样的模组。


--------------------------------------------------------------------------------

在哪里可以找引入档来做 ioctl() 或 syscall()?
以前这些档案会由标准 perl 发行中所附的 h2ph 工具来产生。这个程式将 C 标 头档案里的 cpp(1)指令转换成内含副程式定义的档案,像 &SYS_getitimer,你可 以把它当做函数的参数。这样做并不怎麽完美,但通常可达成任务。简单的像 errno.h 、syscall.h 和socket.h 这些档案都没问题,但像 ioctl.h 这种较难的档案总是需要人工编辑。以下是安装 *.ph 档案的步骤:

1. 进入最高使用者帐户
2. cd /usr/include
3. h2ph *.h */*.h

如果你的系统支援动态载入,那麽为了可携性、而且合理的做法是使用 h2xs(也 是 perl的标准配备)。这个工具将 C 标头档案转换成 Perl 的衍伸档案 (extensions)。 h2xs 的入门要看 perlxstut 。

如果你的系统不支援动态载入,你可能仍应使用 h2xs。参看 perlxstut 和 MakeMaker (简单来说,就是用 make perl 、而非 make来重 建一份使用新的静态连结的 perl)。


--------------------------------------------------------------------------------

为何 setuid perl 程式会抱怨关於系统核心的问题?
有些作业系统的核心有臭虫使得 setuid 程式在先天上就不安全。Perl提供你一些 方法(在 perlsec 里有写)可跳过这些系统的缺陷。


--------------------------------------------------------------------------------

如何打开对某程式既输入又输出的管道 (pipe)?
IPC::Open2 模组(perl 的标准配件)是个好用的方法,它在内部是藉着pipe()、 fork() 和 exec() 来完成此工作。不过切记要读它文件里关於锁死的警告 ( Open2 )。


--------------------------------------------------------------------------------

为何用 system() 却得不到一个指令的输出呢?
你把 system() 和反向引号 (``) 的用法搞混了。 system() 会执行一个指令然後 传回指令结束时的状况资讯(以一个 16 进位值表示:低位元是程序中止所收到的 讯号,高位元才是真正离开时的传回值)。反向引号 (``) 执行一个指令并且把它 所送出的东西送到 STDOUT。

$exit_status = system("mail-users";
$output_string = `ls`;


--------------------------------------------------------------------------------

如何补捉外部指令的 STDERR?
有叁种基本方式执行外部指令:

system $cmd; # 使用 system()
$output = `$cmd`; # 使用 反向引号 (``)
open (PIPE, "cmd |"; # 使用 open()

在 system() 下,STDOUT 和 STDERR 都会输出到和 script 本身的 STDOUT, STDERR相同的出处,除非指令本身将它们导向它处。反向引号和 open() 则 只 读取指令的 STDOUT 部份。

在上述方法中,你可以在呼叫前更改档案描述元 (file descriptor) 名称:

open(STDOUT, ">;logfile";
system("ls";

或者使用 Bourne shell 的档案描述元重导功能:

$output = `$cmd 2>;some_file`;
open (PIPE, "cmd 2>;some_file |";

也可以用档案描述元重导功能将 STDERR 导向到 STDOUT:

$output = `$cmd 2>;&1`;
open (PIPE, "cmd 2>;&1 |";

注意你 不能 光是将 STDERR 开成 STDOUT 的复制,而不呼叫 shell来做这个 重导的工作。这样是不行的:

open(STDERR, ">;&STDOUT";
$alloutput = `cmd args`; # stderr 仍然会跑掉

失败的原因是,open() 让 STDERR 在呼叫 open() 时往 STDOUT的方向走。然後反 向引号让 STDOUT的内容跑到一个字串变数里,但是没有改变 STDERR 的去向(它 仍然往旧的 STDOUT那里跑)。

注意,在反向引号里你 必须 使用 Bourne shell (sh(1)) 重导的语法而非 csh(1)的!至於为何 Perl 的 system()、反向引号和开管道都用 Bourne shell语 法的原因,可在下址找到: http://www.perl.com/CPAN/doc/FMTEYEWTK/versus/csh.whynot

你也可以使用 IPC::Open3 模组(perl 标准配备),但注意它的参数顺序和 IPC::Open2不一样(参看 Open3 )。


--------------------------------------------------------------------------------

为何当管道开启失败时 open() 不会传回错误讯息?
其实会,只是或许并非以你期望的方式。在遵循标准 fork()/exec() 机制的系统 上(例如,Unix),运作原理是这样的:open() 导致一个 fork()。在父程序里, open()传回子程序的ID。然後子程序 exec() 从管道传来/出 的指令。父程序无 法得知 exec() 的动作成功与否-它能传回的只有 fork() 动作成功与否的消息。 要找出这指令是否顺利执行,你得补捉 SIGCHLD讯号并 wait() 以得到子程序离开 时的状态。如果你要写资料到子程序,则 SIGPIPE也该一并捕捉-否则在你写入之 前可能无法察觉 exec() 动作已失败了。这些在 perlipc 文件里都有说明。

在使用 spawn() 机制的系统里,open() 也许 能达到你所期望的-除非 perl 使用一个 shell 来起始你的指令。在这情况下以上对 fork()/exec() 的描述仍适 用。


--------------------------------------------------------------------------------

在输出值是空的情境里使用反向引号有何不对?
严格说起来,没啥不对。但从程式写作严谨与否来说,这样无法写出较易维护的程 式码,因为反向引号有一个(可能很巨大的)传回值,而你却忽略它。同时这也是 缺乏效率的方法,因为你得把每行所有的输出读进来、留一块记忆体给它们,然後 再把它们丢开。人们常常做下列这种事:

`cp file file.bak`;

然後它们就会想:「嘿,乾脆以後都用反向引号来执行程式好了。」这是馊主意, 因为反向引号的目的在补捉程式的输出;system() 函数才是用来执行程式的。

再看看下列这一行:

`cat /etc/termcap`;

你还没有指定输出,所以它会浪费记忆体(就那麽一下子)。另外你也忘了检查 $? 看看程式是否正确的执行。即使你写成

print `cat /etc/termcap`;

但在大部份情况下,这本来可以、而且也应该写成

system("cat /etc/termcap") == 0
or die "cat program failed!";

这样可快速地得到输出(一产生出来就会得到,不用等到最後),并且检查传回值。

system() 同时具有直接决定是否先做 shell 万用字元 (wildcard)处理的功能, 反向引号就不行。


--------------------------------------------------------------------------------

如何不经过 shell 处理来呼叫反向引号?
这需要些技巧。本来是写成

@ok = `grep @opts '$search_string' @filenames`;

你得改成:

my @ok = ();
if (open(GREP, "-|")) {
while () {
chomp;
push(@ok, $_);
}
close GREP;
} else {
exec 'grep', @opts, $search_string, @filenames;
}

一如 system(),当你 exec() 一个序列时不会有 shell 解译的情况发生。


--------------------------------------------------------------------------------

为何给了 EOF(Unix 上是 ^D,MS-DOS 上是 ^Z)後我的程式就不能从 STDIN 读取东西了呢?
因为某些 stdio 的 set error 和 eof 旗标需要清除。你可以用 POSIX 模组里定 义的clearerr()。这是在技术上正确的解决之道。还有一些较不保险的方法:

试着保存搜寻指标然後去找它,例如:
$where = tell(LOG);
seek(LOG, $where, 0);

如果那样行不通,试着去 seek() 档案的另一部份然後再找回来。
如果还是行不通,试着 seek() 档案另一个相异的的部份,读点东西,再回去找。
如果依然不行,放弃使用 stdio 改用 sysread。

--------------------------------------------------------------------------------

如何把 shell 程式转成 perl?
学习 Perl 然後重写。说真的,没有简单的转换方式。用 shell 做起来很笨的工 作可以用 Perl 很轻松的做到,而就是这些麻烦之处使得 shell->;perl 转换程式 非常不可能写得出来。在重新撰写程式的过程里,你会认清自己真正要做的工作为 何,也希望能够跳脱 shell 的管线资料流机制 [pipeline datastream paradigm], 这东西虽对某些事情很方便,但也常造成低效率。


--------------------------------------------------------------------------------

perl 能处理 telnet 或 ftp 这种双向互动吗?
试试 Net::FTP、TCP::Client 和 NET::Telnet 模组(CPAN 有)。 http://www.perl.com/CPAN/scripts/netstuff/telnet.emul.shar也有助於模拟 telnet 协定,但是 Net::Telnet 可能较容易使用。

如果你所要做的只是假装 telnet 但又不要起始 telnet 时的沟通程序,那麽以下 这个标准的双程序方式就可以满足你的需要了:

use IO::Socket; # 5.004 才有的新模组
$handle = IO::Socket::INET->;new('www.perl.com:80')
|| die "无法接上 www.perl.com 的 port 80: $!";
$handle->;autoflush(1);
if (fork()) { # XXX: undef 表示失败
select($handle);
print while ; # 将所有从 stdin 来的丢到 socket
} else {
print while ; # 将所有 socket 来的丢到 stdout
}
close $handle;
exit;


--------------------------------------------------------------------------------

如何在 Perl 里达到 Expect 的功能?
很久很久以前,有个叫做 chat2.pl 的程式库(perl 标准配备之一),但一直没 真正完工。现在,你的最佳选择就是从 CPAN 来的 Comm.pl 程式库。


--------------------------------------------------------------------------------

有没有可能将 perl 的指令列隐藏起来,以躲避像 "ps" 之类的程式?
首先要注意的是,如果你的目的是为了安全(例如避免人们偷看到密码),那你应 该重写你的程式,把重要的资讯从参数中剔除。光是隐藏起来不会让你的程式变得 完全安全。

如要真的把看得见的指令列改掉,你可以设定 $0 这个变数值,如同 perlvar 里写的。但这方法并非各种作业系统都适用。像 sendmail之类的背景程式 (daemons) 就将它们的状态放在那儿:

$0 = "orcus [accepting connections]";


--------------------------------------------------------------------------------

我在 perl script 里 {更动目录,更改我的使用环境}。为何这些改变在程式执行完後就消失了呢?如何让我做的修改显露出来?
Unix
严格的说起来,这是做不到的-一个 script 的执行是从启动它的 shell 生出一 个不同的程序来执行。这个程序的任何变动不会反映到它的父程序,只会反映到更 改之後它自己创造出来的子程序。有个 shell 魔术可以让你藉着在 shell 里 eval()你 script 的输出来装出这种效果,在 comp.unix.questions FAQ 里有详 细内容。
VMS
对 %ENV 的更改会持续到 Perl 离开,但是目录更动则不会。

--------------------------------------------------------------------------------

如何关闭一个程序的档案把手而不用等它完成呢?
假设你的系统支援这种功能,那就只要送个适当的讯号给此程序(参看 kill)。通常是先送一个 TERM 讯号,等一下下,然後再送个 KILL 讯号去终结它。


--------------------------------------------------------------------------------

如何 fork 出一个背景执行 (daemon) 程序?
如果你所指的是离线的程序(未与 tty 连线者),那下列的程序据说在大部份的 Unix系统都能用。非 Unix 系统的使用者应该检查 Your_OS:rocess 模组看看有 没有其他的解决方案。

打开 /dev/tty 然後对它用 TIOCNOTTY ioctl。请参考 tty(4) 。
把目录换到 /
重开 STDIN、STDOUT 和 STDERR 使它们不会与旧的 tty 连接。
用下列方法把程式丢到背景:
fork && exit;


--------------------------------------------------------------------------------

如何使我的程式和 sh 及 csh 一起执行?
参看 eg/nih 这 script(perl 原始码发行的一部分)。


--------------------------------------------------------------------------------

如何得知我是否正在互动模式下执行?
问得好。有的时候 -t STDIN 和 -t STDOUT 可以提供线索,有时不行。

if (-t STDIN && -t STDOUT) {
print "Now what? ";
}

在 POSIX 系统中,你可以用以下方法测试你自己的程序群组与现在控制你终端机 的是否相同:

use POSIX qw/getpgrp tcgetpgrp/;
open(TTY, "/dev/tty") or die $!;
$tpgrp = tcgetpgrp(TTY);
$pgrp = getpgrp();
if ($tpgrp == $pgrp) {
print "前景/n";
} else {
print "背景/n";
}


--------------------------------------------------------------------------------

如何让一个缓慢的事件过时?
如同 Signals 和 Camel 书第六章里所描述的,用 alarm() 函数, 或许再配合上一个讯号处理器。你也可以改用 CPAN 里更具弹性的 Sys::AlarmCall 模组来做。


--------------------------------------------------------------------------------

如何设定 CPU 使用限制?
使用 CPAN 里的 BSD::Resource 模组。


--------------------------------------------------------------------------------

在 Unix 系统上如何避免产生僵 程序 (zombies)?
使用 Signals 里面叫 reaper 的程式码,在接到 SIGCHLD 时会呼 叫wait(),或是用 fork 里面写的双 fork 技巧。


--------------------------------------------------------------------------------

如何使用一个 SQL 资料库?
有几个连接 SQL 资料库非常好的界面。参看 http://www.perl.com/CPAN/modules/dbperl/DBD 里的 DBD::* 模组。


--------------------------------------------------------------------------------

如何让 system() 在收到 control-C 後就离开?
做不到。你需要摹仿 system() 呼叫(参看 perlipc 里的范例程式),然後设计一个讯号处理器,让它把 INT 讯号传给子程序。


--------------------------------------------------------------------------------

如何开启一个档案但不阻挡其他程序的阅读?
如果你有幸使用到支援非阻挡性读取的系统(大部份 Unix 般的系统都有支援), 你只需要用 Fcntl 模组里的 O_NDELAY 或 O_NONBLOCK 旗标,配合 sysopen():

use Fcntl;
sysopen(FH, "/tmp/somefile", O_WRONLY|O_NDELAY|O_CREAT, 0644)
or die "can't open /tmp/somefile: $!";


--------------------------------------------------------------------------------

如何安装一个 CPAN 模组?
最简单的方法就是让 CPAN 这个模组替你代劳。这个模组包含在 5.004及以後的版 本中。如要手动安装 CPAN 模组,或是任何按规矩发展的 CPAN模组,遵循以下步 骤:

把原始码解开放到一个暂存区域
perl Makefile.PL

make

make test

make install

如果你用的 perl 版本在编译时没有建入动态连结的功能,那你只消把第叁步 (make)换成 make perl 然後你就会得到一个新的 perl 执行档,里头连 有你新加入的延伸。

在 MakeMaker里面有更多关於建构模组的细节,并参考「如何保有 一份自己的 模组/程式库目录?」这个问题。


--------------------------------------------------------------------------------

如何保有一份自己的 模组/程式库 目录?
当你建构模组时,在产生 Makefiles 时使用 PREFIX 选项:

perl Makefile.PL PREFIX=/u/mydir/perl

然後在执行用到此 模组/程式库 的程式前先设好 PERL5LIB 环境变数(参考 perlrun ),或是用

use lib '/u/mydir/perl';

进一步的资料可在 Perl 的 lib 手册中找到。


--------------------------------------------------------------------------------

如何把我的程式所在位置加入 模组/程式库 搜寻路径?
use FindBin;
use lib "$FindBin:Bin";
use your_own_modules;


--------------------------------------------------------------------------------

如何在执行时添加目录到自己的引入路径中?
以下是我们建议更动引入路径的方法:

PERLLIB 环境变数
PERL5LIB 环境变数
perl -Idir 指令列参数
use lib pragma, as in
use lib "$ENV{HOME}/myown_perllib";

後者特别有用,因为它知道与机器相关的架构。lib.pm 机制模组是从 5.002 版开 始包含在 Perl 里面的。


--------------------------------------------------------------------------------

如何从终端机一次抓进一个按键?如果用 POSIX 模组时又该怎麽做?
#!/usr/bin/perl -w
use strict;
$| = 1;
for (1
exit;

BEGIN {
use POSIX qw(:termios_h);

my ($term, $oterm, $echo, $noecho, $fd_stdin);

$fd_stdin = fileno(STDIN);

$term = POSIX::Termios->;new();
$term->;getattr($fd_stdin);
$oterm = $term->;getlflag();

$echo = ECHO | ECHOK | ICANON;
$noecho = $oterm & ~$echo;

sub cbreak {
$term->;setlflag($noecho);
$term->;setcc(VTIME, 1);
$term->;setattr($fd_stdin, TCSANOW);
}

sub cooked {
$term->;setlflag($oterm);
$term->;setcc(VTIME, 0);
$term->;setattr($fd_stdin, TCSANOW);
}

sub getone {
my $key = '';
cbreak();
sysread(STDIN, $key, 1);
cooked();
return $key;
}

}
END { cooked() }

我的 CGI script可在指令列下执行但无法从浏览器执行。您能不能帮我修修看?
当然,但您恐怕付不起雇我们的签约金

说真的,如果您能够先证明您已读过下列这几个 FAQs ,但遇到的问题并不单纯、非叁言两语即可回答的话,那麽您 post到 comp.infosystems.www.authoring.cgi上(如果是有关 HTTP 、 HTML ,或 CGI通信协定)的问题可能也会得到口气和缓而有用的答覆。表面上看似 Perl,但骨子里是 CGI之类的问题,如果 post到 comp.lang.perl.misc人家可能就不会这麽乐意地接受了。

几个实用的 FAQs 分别是:

http://www.perl.com/perl/faq/idiots-guide.html
http://www3.pair.com/webthing/docs/cgi/faqs/cgifaq.shtml
http://www.perl.com/perl/faq/perl-cgi-faq.html
http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html
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/ 取得。


--------------------------------------------------------------------------------

如何去除文章中的 HTML标签?
最正确(尽管不是最快)的方法是使用 HTML:arse模组(可由 CPAN取得,是所有写 Web程式者必备的 libwww-perl 套件的一部分)。

许多人尝试用简陋的正规表示式来解决这个问题,譬如说像 s///g,但这个式子在很多情况下会失败,因为要处理的字串可能会跨越断行字元,也可能含有被 quote【跳脱】的箭头号,或有 HTML comment出现;再加上一些疏忽,譬如,人们常忘了转换如 '"]*|(['"]).*?/1)*>;//gs

如果您想要更完整的解法,请看叁部曲的 striphtml 程式, http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/striphtml.gz 。


--------------------------------------------------------------------------------

如何萃取 URLs?
一个快速但不完美的做法是

#!/usr/bin/perl -n00
# qxurl - tchrist@perl.com
print "$2/n" while m{

}gsix;

这个版本并不替相对式写法的 URLs 作调整,也不懂代换 bases【】,或如何处理 HTML comments、同时处理同一个标签里的 HREF和 NAME 属性,或接受 URL形式的参数。同时,它要比一个较「完整」、利用 LWP [libwww-perl]模组套件的解法,例如 http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/xurl.gz这个程 式,快上一百倍。


--------------------------------------------------------------------------------

如何从 user端上传资料?如何在另一台机器上开一个档案?
如果是 HTML表格的话,您可以使用 multipart/form-data的编码格式。 CGI.pm(可自 CPAN取得)中的 start_multipart_form()这个 method 就是为此设计的,它和 startform()这个 method 是两回事。


--------------------------------------------------------------------------------

如何在 HTML中做 pop-up menu(跳出式选单)?
用 和 这两个标签。 CGI.pm模组(可由 CPAN取得)对这个 widget【此指跳出式选单这个介面成分】还有许多其他的介面成分都有支援【即有制作动态标签的函式】,其中有些是以巧妙模拟的方 式达成。


--------------------------------------------------------------------------------

如何抓 HTML档案?
有一个方法是,如果您的系统上装有 lynx一类的文字模式的 HTML浏览器的话,那麽可以这麽做:

$html_code = `lynx -source $url`;
$text_data = `lynx -dump $url`;

收录在 CPAN里的 libwww-perl (LWP)模组则提供了更强的方法来做这件事。它不但可钻过 proxies,而且也不需要 lynx:

# print HTML from a URL
use LWP::Simple;
getprint "http://www.sn.no/libwww-perl/";;

# print ASCII from HTML from a URL
use LWP::Simple;
use HTML:arse;
use HTML::FormatText;
my ($html, $ascii);
$html = get("http://www.perl.com/";
defined $html
or die "Can't fetch HTML from http://www.perl.com/";;
$ascii = HTML::FormatText->;new->;format(parse_html($html));
print $ascii;


--------------------------------------------------------------------------------

如何解开或产生 Web上那些冠 %的码?
以下是一个解码的实例:

$string = "http://altavista.digital.com/cgi-bin/query?pg=q&;what=news&fmt=.&q=%2Bcgi-bin+%2Bperl.exe";
$string =~ s/%([a-fA-F0-9]{2})/chr(hex($1))/ge;

编码比较困难一点,因为您不能盲目地把所有非字母数字的字元 (/W)都一律转换成十六进位的跳脱码。很重要的是有特殊意义的字元,如 / 和 ? 便不可以 转换。要做得正确,最简单的方法大概是使用现成的 URI::Escape模组,而不要重新发明轮子。这个模组是 libwww-perl 套件 (LWP)的一部分,可自 CPAN取得。


--------------------------------------------------------------------------------

如何【将 requests】转向到另一页去?
在您的回应中不要使用 Content-Type这个标头,相反地,用 Location: 这个标头。按正式规定,应当 URL: 才是正确的标头。因此 CGI.pm模组(可 由CPAN取得)两个标头都送:

Location: http://www.domain.com/newpage
URI: http://www.domain.com/newpage

要注意的是,由於 servers采用「最高效率化」的运作方式,故在这些标头中如 果使用相对式的 URLs可能会产生奇怪的後果。


--------------------------------------------------------------------------------

如何替网页加上密码?
不一定,要看情况。您需要读您 server的使用手册,或者查查看上头所列的几个 FAQs。


--------------------------------------------------------------------------------

要怎麽用 Perl来编辑 .htpasswd和 .htgroup这两个档案?
HTTPD::UserAdmin 和 HTTPD::GroupAdmin等模组为这些档案提供了统一的物件导向介面,尽管这些档案可能以各种不同的格式储存。这些资料库可能是纯文字格式、 dbm、Berkeley DB或任何 DBI相容的资料库驱动程式 (drivers)。 HTTPD::UserAdmin支援`Basic' 和 `Digest'这两个认证模式所用的档案。以下是 一例:

use HTTPD::UserAdmin ();
HTTPD::UserAdmin
->;new(DB =>; "/foo/.htpasswd"
->;add($username =>; $password);


--------------------------------------------------------------------------------

如何防范使用者藉由填我的 CGI表格来做坏事?
阅读 CGI security FAQ,(可在 http://www-genome.wi.mit.edu/WWW/faqs/www-security-faq.html取得),还有 Perl CGI FAQ,在 http://www.perl.com/CPAN/doc/FAQs/cgi/perl-cgi-faq.html 。

简单一句话:使用 tainting(沾腥?)这项功能(详见 perlsec )。它会让所有不在您的 script中、来路不明的资料(譬如, CGI参数)无法放到 eval 或 system等呼叫中使用。除了使用 tainting之外,绝对不要使用单一参数 的方式下参数给 system()或 exec(),而应将欲执行的指令及其参数放在一个序 列或阵列里面,再传给 system()或 exec(),这样便可避免 globbing【即被 shell先做解译】。


--------------------------------------------------------------------------------

如何解读、萃取 email标头资料?
如果您只需要一个「快而脏」的解法的话,您可以试试这个从再版的 ``Programming Perl''第 222页中拿出来的例子:

$/ = '';
$header = ;
$header =~ s//n/s+/ /g; #将延续行合并成单行
%head = ( UNIX_FROM_LINE, split /^([-/w]+):/s*/m, $header );

譬如说,您若想保留所有 Received栏位资料的话【因 Received栏位通常不止一个】,这个解法便不太行了。一个完整的解法是使用收录在 CPAN的 Mail::Header模组( MailTools 套件的一部分)。


--------------------------------------------------------------------------------

如何解译 CGI表格?
很多人忍不住要自己写程式来处理这部分的工作,所以您们大概都看过一大堆其中有 $ENV{CONTENT_LENGTH} 和 $ENV{QUERY_STRING}的程式码。没错,这麽 写是可以行得通,但事实上也有很多在网路上出没的这类程式根本不能用!

请不要忍不住去重新发明轮子【译者:这是英文的说法 (reinventing the wheels),也就是浪费时间做人家做过的事的意思】。请改用 CGI.pm或 CGI_Lite.pm(可自 CPAN取得)。如果您被困在无模组的 perl1 .. perl4的土地上,您可以试看看 cgi-lib.pl(可至 http://www.bio.cam.ac.uk/web/form.html取得)。


--------------------------------------------------------------------------------

如何验证 email位址?
无法度。

如果没有寄封信到一个位址去试试看它会不会弹回来(即使是这麽做您还得面对停顿的问题),您是无法确定一个位址是否真的存在的。即使您套用 email 标头的标准规格来做检查的依据,您还是有可能会遇到问题,因为有些送得到的位址并不 符合 RFC-822(电子邮件标头的标准)的规定,但有些符合标准的位址却无法投 递。

许多人试图用一个简单的正规表示式,例如 /^[/w.-]+/@([/w.-]/.)+/w+$/来消除一些通常是无效的 email位址。不过,这样做也把很多合格的位址给一起滤掉了,而且对测试一个位址有没有希望投递成功完全没有帮助,所以在此建议大家不要这麽做;不过您可以看看: http://www.perl.com/CPAN/authors/Tom_Christiansen/scripts/ckaddr.gz。这个 script真的彻底地依据所有的 RFC规定来做检验(除了内嵌式 comments外),同时会排除一些您可能不会想送信去的位址(如 Bill Clinton【美国柯林顿总统】或您的 postmaster),然後它会确定位址中的主机名称可在 DNS中找得到。这个 script 跑起来不是很快,但至少有效。

不少 CGI scripts的作者使用另一个替代的方案:用一个简单的正规表示式,(如上头的那个)。如果一个位址能让这个式子对得上的话,那麽就接受这个位址。如果这个位址对不上这个式子的话,便再向使用者讯问,以确定她们填入的这个位 址正确无误。


--------------------------------------------------------------------------------

如何解 MIME/BASE64字串?
MIME-tools套件(可自 CPAN取得)不但可处理这个问题而且有许多其他的功能。有了这个套件,解 BASE64码就变得像这麽容易:

use MIME::base64;
$decoded = decode_base64($encoded);

一个比较直接的解法是先做一点简单的转译,然後使用 unpack()这个函数的 ``u'' 格式:

tr#A-Za-z0-9+/##cd; #去除非 base64字元
tr#A-Za-z0-9+/# -_#; #转换成 uu码格式
$len = pack("c", 32 + 0.75*length); #计算长度字元
print unpack("u", $len . $_); # uu解码後 print


--------------------------------------------------------------------------------

如何根据使用者帐户名称自动合成 email位址?
在支援 getpwuid【UNIX系统呼叫】、 $add('From', 'gnat@frii.com');
$header->;add('Subject', 'Testing');
$header->;add('To', 'gnat@frii.com');
#制作本文
$body = 'This is a test, ignore';
#产生 mail物件
$mail = new Mail::Internet(undef, Header =>; $header, Body =>; /[$body]);
#送出
$mail->;smtpsend or die;


--------------------------------------------------------------------------------

如何找出我的主机名/网域名/IP位址?
长久以来许多 code都很草率地直接呼叫 `hostname`这个程式来取得主机名。 虽然这麽做很方便,但也同时增加了移植到其他平台上的困难。这是一个很典型的 例子,在方便和可移植性之间作抉择,不论选哪一边,必须付出一些牺牲和代价。

Sys::Hostname这个模组(标准 perl发行的一部分)可用来取得机器的名字,然後您便可利用 gethostbyname()这个系统呼叫来找出该机的 IP位址了(假定您的 DNS 运作正常)。

use Socket;
use Sys::Hostname;
my $host = hostname();
my $addr = inet_ntoa(scalar(gethostbyname($name || 'localhost')));

至少在 Unix底下,取得 DNS网域名最简单的方法大概要算是直接从 /etc/resolv.conf这个档案里面找。当然,这麽做的前提是 resolv.conf这个档 案的设定必须照惯例的格式,还有就是这个档案必先存在才行。

(Perl在非 Unix系统下尚需要一有效的方法来测出机器和网域名)


--------------------------------------------------------------------------------

如何抓新闻讨论群的文章或群组名录?
使用 Net::NNTP或 News::NNTPClient模组,两者皆可自 CPAN下载。这些模组 让抓群组名录这类的差事变得这麽容易:

perl -MNews::NNTPClient
-e 'print News::NNTPClient->;;new->;list("newsgroups"'


--------------------------------------------------------------------------------

如何抓/丢 FTP档案?
LWP::Simple模组(可自 CPAN下载)可以抓,但不能丢档案。 Net::FTP模组(也可自 CPAN下载)虽比较复杂,但可用来丢、也能抓档案。


--------------------------------------------------------------------------------

如何用 Perl做 RPC?
有一个 DCE::RPC模组正在发展阶段(但尚未完成)。一旦完成後它会随着 DCE-Perl这个套件发行(可由 CPAN 下载)。至於 ONC::RPC这样的模组则还没听说有人在发展。


在此感谢萧百龄及两只老虎工作室的翻译

分享到:
评论

相关推荐

    Perl 常见问题集

    Perl常见问题集 Perl faqs翻译版 相当全的问题集,官方版本,值得推荐

    Perl 语言常见问题集

    perl语言编程时常见问题解答,编写perl脚本程序的好助手

    perl常见问题解答

    perlfaq:本常问问题集结构综览。 本章。 perlfaq1:概括性的 Perl问题。 非常概括性、高层次的 Perl 问题 perlfaq2: Perl的取得和学习。 有关哪里可找到 Perl的原始码和使用说明、支援、训练课程,及相关事宜...

    两本Perl教程合集下载.zip

    两本Perl教程合集下载.zip PERL中文教程 PERL常见问题集

    2本Perl教程下载.zip

    PERL中文教程 PERL常见问题集

    Perl学习资料打包(510M-Rearranged_By_Bob).part1 

    内容包括:Perl语言入门、Perl学习笔记、Perl网络编程、高级Perl编程和Perl常见问题集等等。 Perl学习资料打包(510M-Rearranged_By_Bob).part1  Perl学习资料打包(510M-Rearranged_By_Bob).part2  文件较大,两个...

    Web服务精髓(中文版)第二卷

    例如,在Solaris上运行的Java程序能够找到并且调用运行的Windows XP上用C#语言写的代码,或者调用运行在Linux 上用Perl写的程序,而不用考虑这个服务如何实现等细节问题。常见的Web服务集是微软新的.NET策略、Sun...

    OTRS-Docsets:OTRS 2.4 3.1 3.2 3.3。 4.0 5.0 6.0与DASH或任何其他Docset Viewer一起使用的Znuny Docsets

    Znuny(最新版本6.0.32) 常见问题解答最新ITSM ITSM核心ITSM变更管理ITSMServiceLevelManagement ITSM配置管理进出口用法通过下载DASH 搜索用户文档集并安装所需的文档集或者签出此仓库通过双击所需的文档集来安装...

    Web服务精髓(中文版)第一卷

    例如,在Solaris上运行的Java程序能够找到并且调用运行的Windows XP上用C#语言写的代码,或者调用运行在Linux 上用Perl写的程序,而不用考虑这个服务如何实现等细节问题。常见的Web服务集是微软新的.NET策略、Sun...

    MySQL 5.1中文手冊

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得数据库和表...

    MySQL 5.1参考手册 (中文版)

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得数据库和表...

    mysql官方中文参考手册

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得数据库和表...

    MYSQL中文手册

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得...

    MySQL 5.1参考手册中文版

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得...

    MySQL 5.1参考手册

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得数据库和表...

    MySQL5.1参考手册官方简体中文版

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得数据库和表...

    sacrebleu:参考BLEU实现,可自动下载测试集并报告版本字符串,以方便跨实验室比较

    它会自动下载常见的WMT测试集并将其处理为纯文本 它会产生一个简短的版本字符串,以方便进行跨纸张比较 它使用WMT()标准标记化技术,正确计算已解密的输出结果的分数 它产生与WMT使用的官方脚本( mteval-v13a.pl...

    mysql5.1中文手册

    使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息...

    MySQL 5.1官方简体中文参考手册

    2.13.3. 使用Perl DBI/DBD接口的问题 3. 教程 3.1. 连接与断开服务器 3.2. 输入查询 3.3. 创建并使用数据库 3.3.1. 创建并选择数据库 3.3.2. 创建表 3.3.3. 将数据装入表中 3.3.4. 从表检索信息 3.4. 获得数据库和表...

Global site tag (gtag.js) - Google Analytics