📄 perlfaq8.html
字号:
<HTML> <HEAD> <TITLE>perlfaq8</TITLE> </HEAD> <BODY background="gback.jpg"><H1>perlfaq8 - perl 常问问题集,第八篇</H1><p><p><H2><A NAME="INDEX">目录</A></H2><!-- INDEX BEGIN --><UL> <LI><A HREF="#_g_W">篇名</A> <LI><A HREF="#_z">概述</A> <UL> <LI><A HREF="#_p_o_b_t_U">如何得知使用者正在哪个作业系统下执行我的 perl 程式?</A> <LI><A HREF="#_exec_H">为什麽 exec() 不会传值回来?</A> <LI><A HREF="#_p_L_">如何对 键盘/萤幕/滑鼠 做些花样?</A> <LI><A HREF="#_p_V_K_X_H">如何向使用者询问密码?</A> <LI><A HREF="#_p_C_g_H">如何对序列埠做读写动作?</A> <LI><A HREF="#_p_f_K_K_X_H">如何逆解加密後的密码档案?</A> <LI><A HREF="#_p_I_H">如何启动一个背景执行的程序?</A> <LI><A HREF="#_p_r_T_H">如何捕捉 控制字元/讯号?</A> <LI><A HREF="#_p_Unix_t_W_K_X_">如何更动 Unix 系统上隐式密码档 (shadow password) 的内容?</A> <LI><A HREF="#_p_w_M_H">如何设定时间和日期?</A> <LI><A HREF="#_p_w_p_sle">如何能够针对小於一秒的时间做 sleep() 或 alarm() 的动作呢?</A> <LI><A HREF="#_p_q_p_H">如何测量小於一秒的时间?</A> <LI><A HREF="#_p_atexit_setjmp_long">如何做 atexit() 或 setjmp()/longjmp() 的动作?(例外处理)</A> <LI><A HREF="#_sockets_b_System_V">为何我的 sockets 程式在 System V (Solaris) 系统下不能用?「不支援本协定」这个错误讯息又是什麽意思?</A> <LI><A HREF="#_p_q_Perl_I_s_t_W_S_C">如何从 Perl 里呼叫系统中独特的 C 函数?</A> <LI><A HREF="#_b_i_H_J_ioctl_">在哪里可以找引入档来做 ioctl() 或 syscall()?</A> <LI><A HREF="#_setuid_perl_">为何 setuid perl 程式会抱怨关於系统核心的问题?</A> <LI><A HREF="#_p_Y_J_J_S_X_">如何打开对某程式既输入又输出的管道 (pipe)?</A> <LI><A HREF="#_system_o_o_O">为何用 system() 却得不到一个指令的输出呢?</A> <LI><A HREF="#_p_O_STDERR_H">如何补捉外部指令的 STDERR?</A> <LI><A HREF="#_D_open_">为何当管道开启失败时 open() 不会传回错误讯息?</A> <LI><A HREF="#_b_X_O_V_">在输出值是空的情境里使用反向引号有何不对?</A> <LI><A HREF="#_p_g_L_shell_B_z_I_s_V_">如何不经过 shell 处理来呼叫反向引号?</A> <LI><A HREF="#_F_EOF_Unix_W_O_D_AMS_D">为何给了 EOF(Unix 上是 ^D,MS-DOS 上是 ^Z)後我的程式就不能从 STDIN 读取东西了呢?</A> <LI><A HREF="#_p_shell_perl_H">如何把 shell 程式转成 perl?</A> <LI><A HREF="#perl_B_z_telnet_ftp_o_">perl 能处理 telnet 或 ftp 这种双向互动吗?</A> <LI><A HREF="#_p_b_Perl_F_Expect_">如何在 Perl 里达到 Expect 的功能?</A> <LI><A HREF="#_S_i_N_perl_O_C_">有没有可能将 perl 的指令列隐藏起来,以躲避像 "ps" 之类的程式?</A> <LI><A HREF="#_b_perl_script_A_">我在 perl script 里 {更动目录,更改我的使用环境}。为何这些改变在程式执行完後就消失了呢?如何让我做的修改显露出来?</A> <LI><A HREF="#_p_">如何关闭一个程序的档案把手而不用等它完成呢?</A> <LI><A HREF="#_p_fork_X_I_daemon">如何 fork 出一个背景执行 (daemon) 程序?</A> <LI><A HREF="#_p_M_sh_csh_">如何使我的程式和 sh 及 csh 一起执行?</A> <LI><A HREF="#_p_o_O_b_U_">如何得知我是否正在互动模式下执行?</A> <LI><A HREF="#_p_w_C_L_H">如何让一个缓慢的事件过时?</A> <LI><A HREF="#_p_w_CPU_H">如何设定 CPU 使用限制?</A> <LI><A HREF="#_b_Unix_t_W_p_K_">在 Unix 系统上如何避免产生僵 程序 (zombies)?</A> <LI><A HREF="#_p_SQL_w_H">如何使用一个 SQL 资料库?</A> <LI><A HREF="#_p_system_b_control_C">如何让 system() 在收到 control-C 後就离开?</A> <LI><A HREF="#_p_L_">如何开启一个档案但不阻挡其他程序的阅读?</A> <LI><A HREF="#_p_w_CPAN_H">如何安装一个 CPAN 模组?</A> <LI><A HREF="#_p_O_v_w_">如何保有一份自己的 模组/程式库 目录?</A> <LI><A HREF="#_p_b_m_J_">如何把我的程式所在位置加入 模组/程式库 搜寻路径?</A> <LI><A HREF="#_p_b_K_v_J">如何在执行时添加目录到自己的引入路径中?</A> </UL> <LI><A HREF="#_p_q_i_H_p">如何从终端机一次抓进一个按键?如果用 POSIX 模组时又该怎麽做?</A> <LI><A HREF="#_B_P_v">作者、译者与版权</A></UL><!-- INDEX END --><HR><P><H1><A NAME="_g_W">篇名</A></H1>perlfaq8 - 系统互动(原文版 Revision: 1.21, Date: 1997/04/24 22:44:19. 中文版 $Revision: 1.1 $, $Date: 1998/03/25 03:19:06 $)<P><P><HR><H1><A NAME="_z">概述</A></H1>这部份的 Perl 常见问题集涵盖关於与作业系统互动的问题。这包括了程序间通讯 [interprocess communication (IPC)]、使用者介面的控制(键盘、萤幕以及指标 装置),以及几乎所有和资料处理无关的事情。<P>请阅读特别针对你所使用的作业系统下的 perl 所写的常见问题集和文件(例如,<EM>perlvms</EM>、<EM>perlplan9</EM>,...),以取得 perl 在个别差异方面更详尽的资料。<P><P><HR><H2><A NAME="_p_o_b_t_U">如何得知使用者正在哪个作业系统下执行我的 perl 程式?</A></H2><FONT SIZE=-1>$^O</FONT> 这个变数(若使用 English 模组就是 $OSTYPE)会指出你的 perl 解译器执 行档是替哪个作业系统、平台所建的。<P><P><HR><H2><A NAME="_exec_H">为什麽 exec() 不会传值回来?</A></H2>因为这正是它所做的:它用另一个不同的程式来取代你当时所执行的。如果你的程 式需要继续跑下去(这可能正是你问此问题的原因吧?),改用 <CODE>system()</CODE> 。<P><P><HR><H2><A NAME="_p_L_">如何对 键盘/萤幕/滑鼠 做些花样?</A></H2>连接/控制 键盘、萤幕和指标装置(「滑鼠」)的方法因作业系统的不同而有不 同;不妨试试下列模组:<P><DL><DT><STRONG><A NAME="item__L">键盘</A></STRONG><DD><PRE> Term::Cap perl 标准内建模组 Term::ReadKey CPAN Term::ReadLine::Gnu CPAN Term::ReadLine::Perl CPAN Term::Screen CPAN</PRE><P><DT><STRONG><A NAME="item__">萤幕</A></STRONG><DD><PRE> Term::Cap perl 标准内建模组 Curses CPAN Term::ANSIColor CPAN</PRE><P><DT><STRONG><A NAME="item__">滑鼠</A></STRONG><DD><PRE> Tk CPAN</PRE><P></DL><P><HR><H2><A NAME="_p_V_K_X_H">如何向使用者询问密码?</A></H2>(这个问题跟全球资讯网一点关系也没有。如果你要找的是跟 <FONT SIZE=-1>WWW</FONT> 有关的,那就 看另一份常见问题集吧。)<P>【译注:中文版的 Perl <FONT SIZE=-1>CGI</FONT> 程式设计常见问题集可以在下列网址中找到:<AHREF="../../tppmsgs/msgs1.htm#114" tppabs="http://www.math.ncu.edu.tw/~chenym/FAQ/Perl/perl-cgi-faq/">http://www.math.ncu.edu.tw/~chenym/FAQ/Perl/perl-cgi-faq/</A><BR><AHREF="../../tppmsgs/msgs1.htm#115" tppabs="http://2tigers.net/perl/perl-cgi-faq-chi/">http://2tigers.net/perl/perl-cgi-faq-chi/</A> 】<P>在 <A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#crypt">crypt</A> 里面有个范例。首先,将你的终端机设为「无回应」[no echo] 模式,然後就用平常的方法将密码读入。你可以用老式的 <CODE>ioctl()</CODE> 函数、 <FONT SIZE=-1>POSIX</FONT> 终端机控制函数(参看 <A HREF="../../tppmsgs/msgs1.htm#116" tppabs="http://www.perl.org/CPAN/doc/manual/html/lib/POSIX.html#">POSIX</A>,和 Camel 书第七章),或是呼叫 <STRONG>stty</STRONG> 程式,这些方法的可携性/移植性程度都不一样。<P>你也可以在大部份系统上使用 <FONT SIZE=-1>CPAN</FONT> 里的 Term::ReadKey 模组,这个模组较易使用而且理论上也较据可携性/移植性。<P><P><HR><H2><A NAME="_p_C_g_H">如何对序列埠做读写动作?</A></H2>这端看你在什麽作业系统上执行你的程式。以 Unix 来说,序列埠可以透过 /dev 目录下的档案来撷取;而在其他系统上,设备的名称无疑地会不一样。以下是一些在设备互动时可能遭遇的共同问题:<P><DL><DT><STRONG><A NAME="item__">锁档 (lockfiles)</A></STRONG><DD>你的系统可能会使用锁档来控制多重读写的情况。确定你用的是正确的协定。因为当多个程序同时对一个装置做读取时可能会发生意想不到的情况。<P><DT><STRONG><A NAME="item__">开档模式</A></STRONG><DD>如果你打算对一个装置同时做读与写的动作,你得将它开到更新的模式( 在<A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#open">open</A> 里有更详细的解说)。如果你不希望冒着阻挡其他程序读取 这个装置的风险,那就得用 <CODE>sysopen()</CODE> 和 Fcntl 模组(标准 perl 的一部分)内 的 <CODE>O_RDWR|O_NDELAY|O_NOCTTY</CODE>。在 <A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#sysopen">sysopen</A> 里有对此方法更 详尽的解说。<P><DT><STRONG><A NAME="item__">档案尾</A></STRONG><DD>有些装置会等着在每行结尾处看到一个 ``\r'',而非 ``\n''。在某些平台上的 perl, ``\r''和 ``\n'' 与它们平常(在 Unix 上)所指的 <FONT SIZE=-1>ASCII</FONT> 值 ``\015'' 和 ``\012'' 有 所不同。你也许得直接给定数值,例如用八进位 (``\015'')、十六进位 (``0x0D''), 或指定控制字元 (``\cM'')。<P><PRE> print DEV "atv1\012"; # 对某些装置来说是错误的 print DEV "atv1\015"; # 对某些装置来说是对的</PRE><P>尽管对普通的文字档案,一个 ``\n'' 便可解决断行的问题,但目前在不同作业系统 间(Unix、DOS/Win 和 Macintosh),对於断行记号仍无统一标准,而只有用 ``\015\012'' 来当成 <EM>每行</EM>的结尾,然後再视需要去掉输出中不想要的部份。这 个做法尤其常用於 socket输出/输入 与自动洗清 (autoflushing),也是接下来 要讨论的主题。<P><DT><STRONG><A NAME="item__M_X">洗清输出</A></STRONG><DD>如果你希望 <CODE>print()</CODE>的时候每个字元都要送到你指定的装置去,那你应自动清洗你的档案把手,旧方法是:<P><PRE> use FileHandle; DEV->autoflush(1);</PRE><P>比较新的方法是:<P><PRE> use IO::Handle; DEV->autoflush(1);</PRE><P>你可以用 <CODE>select()</CODE> 和 <CODE>$|</CODE> 变数来控制自动清洗的动作(参考 <A HREF="../../tppmsgs/msgs1.htm#100" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlvar.html#_">$|</A> 和<A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#select">select</A>):<P><PRE> $oldh = select(DEV); $| = 1; select($oldh);</PRE><P>你也可能看到不使用额外的暂存变数的写法,例如:<P><PRE> select((select(DEV), $| = 1)[0]);</PRE><P>如同前一个项目所说的,这方法对 Unix 和 Macintosh 间的 socket 输出/入 没 用。在这种情况下,你得把你的行末字元写死在程式码内。<P><DT><STRONG><A NAME="item__J">不挡式输入 (non-blocking input)</A></STRONG><DD>如果你正在做一个具阻挡性的 <CODE>read()</CODE> 或 <CODE>sysread()</CODE>动作,则你需要安排一个闹 铃把手或提供一个逾时设定(参看 <A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#alarm">alarm</A>)。如果你是用非阻挡式的 开档,那麽就要配合非阻挡性的读取,也就是说得用到4 个参数的 <CODE>select()</CODE> 来确 定此装置的 输出/入 是否已准备好了(参考 <A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#select">select</A>)。<P></DL><P><HR><H2><A NAME="_p_f_K_K_X_H">如何逆解加密後的密码档案?</A></H2>花大把大把的钱去买破解专用的硬体,这会让你成为焦点话题。<P>说正经的,如果是碰到 Unix 密码档的话就不行 - Unix 密码系统用的是单向的加 密函数。像 Crack 之类的程式可以暴力地(并聪明地)试着猜出密码,但无法 (也不能)保证速战速决。<P>如果你耽心的是使用者选取不良的密码,你应该在使用者换密码时主动审核(例如说修改 <CODE>passwd(1)</CODE> 程式加入这个功能)。<P><P><HR><H2><A NAME="_p_I_H">如何启动一个背景执行的程序?</A></H2>你可以使用:<P><PRE> system("cmd &")</PRE><P>或是用 fork,像 <A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#fork">fork</A> 里写的(在 <A HREF="../../tppmsgs/msgs0.htm#28" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlipc.html#">perlipc</A> 里有更进一步的 范例)。如果你在 Unix 类的系统上的话,请注意以下几件事情:<P><DL><DT><STRONG><A NAME="item_STDIN">STDIN, STDOUT 和 STDERR 是共享的</A></STRONG><DD>主程序和背景程序(即「子」程序)共用同一个 STDIN、STDOUT 和 <FONT SIZE=-1>STDERR</FONT> 档案 把手。如果两个程序想同时去读、写同一个档案把手,就可能有怪事会发生。你也 许应该替子程序关闭或重新开启这些把手。你可以用开启一个管道 (pipe) 的方法 避免这些问题(参看<A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#open">open</A>)但是在某些系统上这样做会强迫子程序 必须比父程序早死。<P><DT><STRONG><A NAME="item__T_">讯号</A></STRONG><DD>SIGCHLD、可能还有 <FONT SIZE=-1>SIGPIPE</FONT> 这两个讯号要抓到。当背景程序执行完成後就会送出 <FONT SIZE=-1>SIGCHLD</FONT> 讯号。而当你写入一个子程序已经关闭的档案把手时就会收到 <FONT SIZE=-1>SIGPIPE</FONT> 讯号(一个未抓住的 <FONT SIZE=-1>SIGPIPE</FONT> 可能导致你的程式无声无息地死去)。用 <CODE>system("cmd&")</CODE> 的话不会有这样的问题。<P><DT><STRONG><A NAME="item__">僵 程序</A></STRONG><DD>你得做准备,在子程序结束时「收成」它:<P><PRE> $SIG{CHLD} = sub { wait };</PRE><P>在 <A HREF="../../tppmsgs/msgs0.htm#28" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlipc.html#Signals">Signals</A> 有范例程式教你怎麽做。用 <CODE>system("prog &")</CODE> 的 话不会有僵 程序的问题。<P></DL><P><HR><H2><A NAME="_p_r_T_H">如何捕捉 控制字元/讯号?</A></H2>你并不能真的 ``捕捉'' 一个控制字元。而是控制字元产生一个讯号让你捕捉。关於讯号的资料可以在 <A HREF="../../tppmsgs/msgs0.htm#28" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlipc.html#Signals">Signals</A> 以及 Camel 书第六章里找到。<P>要小心的是,大多 <FONT SIZE=-1>C</FONT> 程式库无法重新进入 [re-entrant]。因此当你要尝试着在一 个处理器里做 <CODE>print()</CODE> 动作,而这个处理器是由另一个stdio 的动作所叫出来的 话,你的内部结构可能会处於失调状态,而程式可能会丢出记忆核心 (dump core)。 有的时候你可以用 <CODE>syswrite()</CODE> 取代 <CODE>print()</CODE> 以避免这个状况。<P>除非你极为小心,否则在一个讯号处理器中,唯一安全可做的是:设定一个变数後离开。而在第一个情况下,你在设定变数的时候应确定 <CODE>malloc()</CODE>不会被叫出来 (譬如,设定一个已经有值的变数)。<P>例如:<P><PRE> $Interrupted = 0; # 确定它有个值 $SIG{INT} = sub { $Interrupted++; syswrite(STDERR, "哇\n", 5); }</PRE><P>然而,因为系统呼叫会自己重新启动,你将会发现如果你用的是「慢的」呼叫,像<<FONT SIZE=-1>FH</FONT>>、read()、connect() 或 <CODE>wait(),那麽将它们停下的唯一办法是使</CODE> 用「跳远」的方式跳出来;也就是产生一个例外讯号。参看在 <A HREF="../../tppmsgs/msgs0.htm#28" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlipc.html#Signals">Signals</A> 里对阻挡性 <CODE>flock()</CODE> 的逾时处理器的说明,或骆驼书第六 章。<P><P><HR><H2><A NAME="_p_Unix_t_W_K_X_">如何更动 Unix 系统上隐式密码档 (shadow password) 的内容?</A></H2>如果你的 perl 安装正确的话,在 <A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#">perlfunc</A> 里描述的 getpw*() 函数应该就能够读取隐式密码档了(只有读取权)。要更动该档案内容,做一个新的密码档(这个档案的格式因系统而异,请看 <EM>passwd(5)</EM>)然後用 <CODE>pwd_mkdb(8)(参考</CODE> <EM>pwd_mkdb(5)</EM>)来安装新的密码档。<P><P><HR><H2><A NAME="_p_w_M_H">如何设定时间和日期?</A></H2>假设你有足够的权限,你应该可以用 <CODE>date(1)</CODE> 程式来设定系统的时间与日期。 (但没有针对个别程序修改时间日期的方法)这机制在 Unix、MS-DOS、Windows 和 <FONT SIZE=-1>NT</FONT> 下都能用;VMS 下则要用 <CODE>set time</CODE>。<P>然而,如果你只是要更动你的时区,只消设定一个环境变数即可:<P><PRE> $ENV{TZ} = "MST7MDT"; # unix 下 $ENV{'SYS$TIMEZONE_DIFFERENTIAL'}="-5" # vms system "trn comp.lang.perl";</PRE><P><P><HR><H2><A NAME="_p_w_p_sle">如何能够针对小於一秒的时间做 sleep() 或 alarm() 的动作呢?</A></H2>如果你要比 <CODE>sleep()</CODE>所提供的最小单位一秒更精细的话,最简单的方法就是用 <A HREF="../../tppmsgs/msgs0.htm#68" tppabs="http://www.perl.org/CPAN/doc/manual/html/pod/perlfunc.html#select">select</A> 里面写的 <CODE>select()</CODE> 函数。如果你的系统有 itimers 并支援syscall(),你可以试试下面这个老范例 <AHREF="../../tppmsgs/msgs1.htm#117" tppabs="http://www.perl.com/CPAN/doc/misc/ancient/tutorial/eg/itimers.pl">http://www.perl.com/CPAN/doc/misc/ancient/tutorial/eg/itimers.pl</A>.<P><P><HR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -