Provided by: manpages-zh_1.6.3.2-1_all bug

NAME

       perlfaq8 - 系統交互 (2003/01/26 17:44:04 )

DESCRIPTION 描述

       Perl FAQ 的這一節覆蓋了與系統交互有關的問題。主題包括進程間通信 (IPC),用戶界面控制
       (鍵盤,屏幕和指點設備),以及其他與數據操作不相關的事項

       閱讀你係統中的 perl 自帶的 FAQ 和文檔 (例如,perlvms,perlplan9...)。它們會包含有關你的
       perl 版本的更詳細的信息。

       如何找出正在運行的操作系統?

       爲什麼 exec() 不返回?

       因爲這正是它所做的:它用另一個不同的程式來取代你當時所執行的。如果你的程
       式需要繼續跑下去(這可能正是你問此問題的原因吧?),改用 system() 。

       如何對鍵盤/螢幕/滑鼠做些花樣?

       連接/控制 鍵盤、螢幕和指標裝置(「滑鼠」)的方法因作業系統的不同而有不
       同;不妨試試下列模組:

       Keyboard
               Term::Cap                   標準內建模組
               Term::ReadKey               CPAN
               Term::ReadLine::Gnu         CPAN
               Term::ReadLine::Perl        CPAN
               Term::Screen                CPAN

       Screen
               Term::Cap                   標準內建模組
               Curses                      CPAN
               Term::ANSIColor             CPAN

       Mouse
               Tk                          CPAN

       Some of these specific cases are shown below.

       How do I print something out in color?

       In general, you don't, because you don't know whether the recipient has a color-aware
       display device.  If you know that they have an ANSI terminal that understands color, you
       can use the Term::ANSIColor module from CPAN:

           use Term::ANSIColor;
           print color("red"), "Stop!\n", color("reset");
           print color("green"), "Go!\n", color("reset");

       Or like this:

           use Term::ANSIColor qw(:constants);
           print RED, "Stop!\n", RESET;
           print GREEN, "Go!\n", RESET;

       How do I read just one key without waiting for a return key?

       Controlling input buffering is a remarkably system-dependent matter.  On many systems, you
       can just use the stty command as shown in "getc" in perlfunc, but as you see, that's
       already getting you into portability snags.

           open(TTY, "+</dev/tty") or die "no tty: $!";
           system "stty  cbreak </dev/tty >/dev/tty 2>&1";
           $key = getc(TTY);           # perhaps this works
           # OR ELSE
           sysread(TTY, $key, 1);      # probably this does
           system "stty -cbreak </dev/tty >/dev/tty 2>&1";

       The Term::ReadKey module from CPAN offers an easy-to-use interface that should be more
       efficient than shelling out to stty for each key.  It even includes limited support for
       Windows.

           use Term::ReadKey;
           ReadMode('cbreak');
           $key = ReadKey(0);
           ReadMode('normal');

       However, using the code requires that you have a working C compiler and can use it to
       build and install a CPAN module.  Here's a solution using the standard POSIX module, which
       is already on your systems (assuming your system supports POSIX).

           use HotKey;
           $key = readkey();

       And here's the HotKey module, which hides the somewhat mystifying calls to manipulate the
       POSIX termios structures.

           # HotKey.pm
           package HotKey;

           @ISA = qw(Exporter);
           @EXPORT = qw(cbreak cooked readkey);

           use strict;
           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);  # ok, so i don't want echo either
               $term->setcc(VTIME, 1);
               $term->setattr($fd_stdin, TCSANOW);
           }

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

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

           END { cooked() }

           1;

       How do I check whether input is ready on the keyboard?

       The easiest way to do this is to read a key in nonblocking mode with the Term::ReadKey
       module from CPAN, passing it an argument of -1 to indicate not to block:

           use Term::ReadKey;

           ReadMode('cbreak');

           if (defined ($char = ReadKey(-1)) ) {
               # input was waiting and it was $char
           } else {
               # no input was waiting
           }

           ReadMode('normal');                  # restore normal tty settings

       How do I clear the screen?

       If you only have do so infrequently, use "system":

           system("clear");

       If you have to do this a lot, save the clear string so you can print it 100 times without
       calling a program 100 times:

           $clear_string = `clear`;
           print $clear_string;

       If you're planning on doing other screen manipulations, like cursor positions, etc, you
       might wish to use Term::Cap module:

           use Term::Cap;
           $terminal = Term::Cap->Tgetent( {OSPEED => 9600} );
           $clear_string = $terminal->Tputs('cl');

       How do I get the screen size?

       If you have Term::ReadKey module installed from CPAN, you can use it to fetch the width
       and height in characters and in pixels:

           use Term::ReadKey;
           ($wchar, $hchar, $wpixels, $hpixels) = GetTerminalSize();

       This is more portable than the raw "ioctl", but not as illustrative:

           require 'sys/ioctl.ph';
           die "no TIOCGWINSZ " unless defined &TIOCGWINSZ;
           open(TTY, "+</dev/tty")                     or die "No tty: $!";
           unless (ioctl(TTY, &TIOCGWINSZ, $winsize='')) {
               die sprintf "$0: ioctl TIOCGWINSZ (%08x: $!)\n", &TIOCGWINSZ;
           }
           ($row, $col, $xpixel, $ypixel) = unpack('S4', $winsize);
           print "(row,col) = ($row,$col)";
           print "  (xpixel,ypixel) = ($xpixel,$ypixel)" if $xpixel ⎪⎪ $ypixel;
           print "\n";

       如何向使用者詢問密碼?

       (這個問題跟全球資訊網一點關係也沒有。如果你要找的是跟 WWW 有關的,那就
       看另一份常見問題集吧。)

       在 perlfunc 中的 "crypt" 裏面有個範例。首先,將你的終端機設爲「無迴應」"no echo"
       模式,然後就用平常的方法將密碼讀入。你可以用老式的 ioctl() 函數、 POSIX
       終端機控制函數(參看 POSIX ,和駱駝書第七章),或是呼叫 stty
       程式,這些方法的可攜性/移植性程度都不一樣。

       你也可以在大部份系統上使用 CPAN 裏的 Term::ReadKey
       模組,這個模組較易使用而且理論上也較據可攜性/移植性。

           use Term::ReadKey;

           ReadMode('noecho');
           $password = ReadLine(0);

       如何讀寫串口?

       這端看你在什麼作業系統上執行你的程式。以 Unix 來說,序列埠可以透過 /dev
       目錄下的檔案來擷取;
       而在其他系統上,設備的名稱無疑地會不一樣。以下是一些在設備互動時可能遭遇的共同問題:

       lockfiles
           你的系統可能會使用鎖檔來控制多重讀寫的情況。確定你用的是正確的協定。因爲當多個程序同時對一個裝置做讀取時可能會發生意想不到的情況。

       open mode
           如果你打算對一個裝置同時做讀與寫的動作,你得將它開到更新的模式( 在 perlfunc 中的 open
           裏有更詳細的解說)。如果你不希望冒着阻擋其他程序讀取 這個裝置的風險,那就得用
           sysopen() 和 Fcntl 模組(標準 perl 的一部分)內 的 "O_RDWR⎪O_NDELAY⎪O_NOCTTY"。在
           perlfunc 中的 sysopen 裏有對此方法更 詳盡的解說。

       end of line
           有些裝置會等着在每行結尾處看到一個 "\r",而非 "\n"。在某些平臺上的 perl, "\r"和 "\n"
           與它們平常(在 Unix 上)所指的 ASCII 值 "\015" 和 "\012" 有
           所不同。你也許得直接給定數值,例如用八進位 ("\015")、十六進位 ("0x0D"),
           或指定控制字元 ("\cM")。

               print DEV "atv1\012";       # wrong, for some devices
               print DEV "atv1\015";       # right, for some devices

           儘管對普通的文字檔案,一個 "\n" 便可解決斷行的問題,但目前在不同作業系統
           間(Unix、DOS/Win 和 Macintosh),對於斷行記號仍無統一標準,而只有用 "\015\012" 來當成
           每行的結尾,然後再視需要去掉輸出中不想要的部份。這 個做法尤其常用於 socket輸出/輸入
           與自動刷新 (autoflushing),也是接下來 要討論的主題。

       flushing output
           如果你希望 print()
           的時候每個字元都要送到你指定的裝置去,那你應自動刷新文件句柄。可以使用 select() 和 $⎪
           變量控制自動刷新,參見 perlvar 中的 "$⎪" 和 perlfunc 中的 "select",或 perlfaq5, ``How
           do I flush/unbuffer an output filehandle?  Why must I do this?''):

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

           你也可能看到不使用額外的暫存變數的寫法,例如:

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

           Or if you don't mind pulling in a few thousand lines of code just because you're
           afraid of a little $⎪ variable:

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

           As mentioned in the previous item, this still doesn't work when using socket I/O
           between Unix and Macintosh.  You'll need to hard code your line terminators, in that
           case.

       non-blocking input
           如果你正在做一個阻塞的 read() 或 sysread() 動作,則你需要安排一個鬧
           鈴把手或提供一個逾時設定(參看 alarm)。如果你是用非阻擋式的
           開檔,那麼就要配合非阻擋性的讀取,也就是說得用到4 個參數的 select() 來確 定此裝置的
           輸出/入 是否已準備好了(參考 perlfunc 中的 select )。

       While trying to read from his caller-id box, the notorious Jamie Zawinski
       <jwz@netscape.com>, after much gnashing of teeth and fighting with sysread, sysopen,
       POSIX's tcgetattr business, and various other functions that go bump in the night, finally
       came up with this:

           sub open_modem {
               use IPC::Open2;
               my $stty = `/bin/stty -g`;
               open2( \*MODEM_IN, \*MODEM_OUT, "cu -l$modem_device -s2400 2>&1");
               # starting cu hoses /dev/tty's stty settings, even when it has
               # been opened on a pipe...
               system("/bin/stty $stty");
               $_ = <MODEM_IN>;
               chomp;
               if ( !m/^Connected/ ) {
                   print STDERR "$0: cu printed `$_' instead of `Connected'\n";
               }
           }

       如何解碼加密的口令文件?

       花大把大把的錢去買破解專用的硬體,這會讓你成爲焦點話題。

       說正經的,如果是碰到 Unix 密碼檔的話就不行 - Unix 密碼系統用的是單向的加 密函數。像 Crack
       之類的程式可以暴力地(並聰明地)試着猜出密碼,但無法 (也不能)保證速戰速決。

       如果你耽心的是使用者選取不良的密碼,你應該在使用者換密碼時主動審覈(例如說修改 passwd(1)
       程式加入這個功能)。

       如何在後臺開啓進程?

       Several modules can start other processes that do not block your Perl program.  You can
       use IPC::Open3, Parallel::Jobs, IPC::Run, and some of the POE modules.  See CPAN for more
       details.

       你可以使用:

           system("cmd &")

       或是用 fork,像 perlfunc 中的 fork 裏寫的(在 perlipc 裏有更進一步的 範例)。如果你在 Unix
       類的系統上的話,請注意以下幾件事情:

       STDIN, STDOUT, and STDERR are shared
           主程序和背景程序(即「子」程序)共用同一個 STDIN、STDOUT 和 STDERR 檔案
           把手。如果兩個程序想同時去讀、寫同一個檔案把手,就可能有怪事會發生。你也
           許應該替子程序關閉或重新開啓這些把手。你可以用開啓一個管道 (pipe) 的方法
           避免這些問題(參看 open)但是在某些系統上這樣做會強迫子程序 必須比父程序早死。

       信號
           SIGCHLD、可能還有 SIGPIPE 這兩個訊號要抓到。當背景程序執行完成後就會送出 SIGCHLD
           訊號。而當你寫入一個子程序已經關閉的檔案把手時就會收到 SIGPIPE 訊號(一個未抓住的
           SIGPIPE 可能導致你的程式無聲無息地死去)。用 system("cmd&") 的話不會有這樣的問題。

       殭屍進程
           你得做準備,在子程序結束時「收成」它:

               $SIG{CHLD} = sub { wait };

               $SIG{CHLD} = 'IGNORE';

           You can also use a double fork. You immediately wait() for your first child, and the
           init daemon will wait() for your grandchild once it exits.

                   unless ($pid = fork) {
                           unless (fork) {
                       exec "what you really wanna do";
                       die "exec failed!";
                           }
                   exit 0;
                   }
               waitpid($pid,0);

           在 Signals 有範例程式教你怎麼做。用 system("prog &") 的 話不會有僵 程序的問題。

       如何截獲控制字符/信號?

       你並不能真的 ``捕捉'' 一個控制字元。而是控制字元產生一個訊號讓你捕捉。關於訊號的資料可以在
       Signals 以及駱駝書第六章裏找到。

       要小心的是,大多 C 程式庫無法重新進入 [re-entrant]。因此當你要嘗試着在一 個處理器裏做
       print() 動作,而這個處理器是由另一個stdio 的動作所叫出來的
       話,你的內部結構可能會處於失調狀態,而程式可能會丟出記憶核心 (dump core)。
       有的時候你可以用 syswrite() 取代 print() 以避免這個狀況。

       除非你極爲小心,否則在一個訊號處理器中,唯一安全可做的是:設定一個變數後離開。而在第一個情況下,你在設定變數的時候應確定
       malloc() 不會被叫出來 (譬如,設定一個已經有值的變數)。

       例如:

           $Interrupted = 0;   # 確定它有個值
           $SIG{INT} = sub {
               $Interrupted++;
               syswrite(STDERR, "ouch\n", 5);
           }

       然而,因爲系統呼叫會自己重新啓動,你將會發現如果你用的是「慢的」呼叫,像 <
       FH>、read()、connect() 或 wait(),那麼將它們停下的唯一辦法是使
       用「跳遠」的方式跳出來;也就是產生一個例外訊號。參看在 Signals 裏對阻擋性 flock()
       的逾時處理器的說明,或駱駝書第六 章。

        Unix 系統中如何修改 shadow 文件?

       如果你的 perl 安裝正確的話,在 perlfunc 裏描述的 getpw*()
       函數應該就能夠讀取隱式密碼檔了(只有讀取權)。要更動該檔案內容,做一個新的密碼檔(這個檔案的格式因系統而異,請看
       passwd(5) )然後用 pwd_mkdb(8)(參考 pwd_mkdb(5))來安裝新的密碼檔。

       如何設置時間和日期?

       假設你有足夠的權限,你應該可以用 date(1) 程式來設定系統的時間與日期。
       (但沒有針對個別程序修改時間日期的方法)這機制在 Unix、MS-DOS、Windows 和 NT 下都能用;VMS
       下則要用 set time 。

       然而,如果你只是要更動你的時區,只消設定一個環境變數即可:

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

       如何 sleep()  alarm() 少於一秒的時間?

       如果你要比 sleep() 所提供的最小單位一秒更精細的話,最簡單的方法就是用 select 裏面寫的
       select() 函數。試一試 Time::HiRes 和 BSD::Itimer 模塊 (可以從 CPAN 下載,從 Perl 5.8 開始
       Time::HiRes 成爲標準發行的一部分).

       如何測度少於一秒的時間?

       一般來說,你可能做不到。 Time::HiRes 模組(CPAN 有,從 Perl 5.8
       開始成爲標準發行的一部分)在某些系統上能達到此 功能。

       總之,你可能做不到。但是如果你的 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: $!";

              ##########################
              # DO YOUR OPERATION HERE #
              ##########################

           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 文件)。

       For example, you can use this to make sure your filter program managed to finish its
       output without filling up the disk:

           END {
               close(STDOUT) ⎪⎪ die "stdout close failed: $!";
           }

       如果當程式被沒有抓到的訊號終結了,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 裏的模組,因爲也許某人已
       經寫了個這樣的模組。 On Windows, try Win32::API.  On Macs, try Mac::Carbon.  If no module
       has an interface to the C function, you can inline a bit of C in your Perl source with
       Inline::C.

       在哪裏可以找引入檔來做 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()
       來完成此工作。不過切記要讀它文件裏關於鎖死的警告 ( 參見 IPC::Open2 )。參見 perlipc 中的
       "Bidirectional Communication with Another Process" 和 "Bidirectional Communication with
       Yourself"

       You may also use the IPC::Open3 module (part of the standard perl distribution), but be
       warned that it has a different order of arguments from IPC::Open2 (see IPC::Open3).

       爲何用 system()卻得不到一個指令的輸出呢?

       你把 system() 和反向引號 (``) 的用法搞混了。 system() 會執行一個指令然後
       傳回指令結束時的狀況資訊(以一個 16 進位值表示:低位元是程序中止所收到的
       訊號,高位元纔是真正離開時的傳回值)。反向引號 (``) 執行一個指令並且把它 所送出的東西送到
       STDOUT。

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

       如何捕捉外部指令的 STDERR?

       有叄種基本方式執行外部指令:

           system $cmd;                # 使用 system()
           $output = `$cmd`;           # 使用 backticks (``)
           open (PIPE, "cmd ⎪");       # 使用 open()

       在 system() 下,STDOUT 和 STDERR 都會輸出到和 script 本身的 STDOUT,
       STDERR相同的出處,除非指令本身將它們導向它處。反向引號和 open() 則 只 讀取指令的 STDOUT
       部份。

       你也可以使用 IPC::Open3 模組.  Benjamin Goldberg provides some sample code:

       To capture a program's STDOUT, but discard its STDERR:

           use IPC::Open3;
           use File::Spec;
           use Symbol qw(gensym);
           open(NULL, ">", File::Spec->devnull);
           my $pid = open3(gensym, \*PH, ">&NULL", "cmd");
           while( <PH> ) { }
           waitpid($pid, 0);

       To capture a program's STDERR, but discard its STDOUT:

           use IPC::Open3;
           use File::Spec;
           use Symbol qw(gensym);
           open(NULL, ">", File::Spec->devnull);
           my $pid = open3(gensym, ">&NULL", \*PH, "cmd");
           while( <PH> ) { }
           waitpid($pid, 0);

       To capture a program's STDERR, and let its STDOUT go to our own STDERR:

           use IPC::Open3;
           use Symbol qw(gensym);
           my $pid = open3(gensym, ">&STDERR", \*PH, "cmd");
           while( <PH> ) { }
           waitpid($pid, 0);

       To read both a command's STDOUT and its STDERR separately, you can redirect them to temp
       files, let the command run, then read the temp files:

           use IPC::Open3;
           use Symbol qw(gensym);
           use IO::File;
           local *CATCHOUT = IO::File->new_tempfile;
           local *CATCHERR = IO::File->new_tempfile;
           my $pid = open3(gensym, ">&CATCHOUT", ">&CATCHERR", "cmd");
           waitpid($pid, 0);
           seek $_, 0, 0 for \*CATCHOUT, \*CATCHERR;
           while( <CATCHOUT> ) {}
           while( <CATCHERR> ) {}

       But there's no real need for *both* to be tempfiles... the following should work just as
       well, without deadlocking:

           use IPC::Open3;
           use Symbol qw(gensym);
           use IO::File;
           local *CATCHERR = IO::File->new_tempfile;
           my $pid = open3(gensym, \*CATCHOUT, ">&CATCHERR", "cmd");
           while( <CATCHOUT> ) {}
           waitpid($pid, 0);
           seek CATCHERR, 0, 0;
           while( <CATCHERR> ) {}

       And it'll be faster, too, since we can begin processing the program's stdout immediately,
       rather than waiting for the program to finish.

       在上述方法中,你可以在呼叫前更改文件描述符 (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 still escapes

       失敗的原因是,open() 讓 STDERR 在呼叫 open() 時往 STDOUT的方向走。然後反 向引號讓
       STDOUT的內容跑到一個字串變數裏,但是沒有改變 STDERR 的去向(它 仍然往舊的 STDOUT那裏跑)。

       注意,在反向引號裏你 必須 使用 Bourne shell (sh(1)) 重導的語法而非 csh(1)的!至於爲何 Perl
       的 system()、反向引號和開管道都用 Bourne shell語法的原因,可在下址找到:"Far More Than You
       Ever Wanted To Know", http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz .
       要同時捕捉一個命令的 STDERR 和 STDOUT:

           $output = `cmd 2>&1`;                       # either with backticks
           $pid = open(PH, "cmd 2>&1 ⎪");              # or with an open pipe
           while (<PH>) { }                            #    plus a read

       To capture a command's STDOUT but discard its STDERR:

           $output = `cmd 2>/dev/null`;                # either with backticks
           $pid = open(PH, "cmd 2>/dev/null ⎪");       # or with an open pipe
           while (<PH>) { }                            #    plus a read

       To capture a command's STDERR but discard its STDOUT:

           $output = `cmd 2>&1 1>/dev/null`;           # either with backticks
           $pid = open(PH, "cmd 2>&1 1>/dev/null ⎪");  # or with an open pipe
           while (<PH>) { }                            #    plus a read

       To exchange a command's STDOUT and STDERR in order to capture the STDERR but leave its
       STDOUT to come out our old STDERR:

           $output = `cmd 3>&1 1>&2 2>&3 3>&-`;        # either with backticks
           $pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-⎪");# or with an open pipe
           while (<PH>) { }                            #    plus a read

       To read both a command's STDOUT and its STDERR separately, it's easiest and safest to
       redirect them separately to files, and then read from those files when the program is
       done:

           system("program args 1>/tmp/program.stdout 2>/tmp/program.stderr");

       Ordering is important in all these examples.  That's because the shell processes file
       descriptor redirections in strictly left to right order.

           system("prog args 1>tmpfile 2>&1");
           system("prog args 2>&1 1>tmpfile");

       The first command sends both standard out and standard error to the temporary file.  The
       second command sends only the old standard output there, and the old standard error shows
       up on the old standard out.

       爲何當管道開啓失敗時 open()不會傳回錯誤訊息?

       If the second argument to a piped open() contains shell metacharacters, perl fork()s, then
       exec()s a shell to decode the metacharacters and eventually run the desired program.  If
       the program couldn't be run, it's the shell that gets the message, not Perl. All your Perl
       program can find out is whether the shell itself could be successfully started.  You can
       still capture the shell's STDERR and check it for error messages.  See "How can I capture
       STDERR from an external command?" elsewhere in this document, or use the IPC::Open3
       module.

       If there are no shell metacharacters in the argument of open(), Perl runs the command
       directly, without using the shell, and can correctly report whether the command started.

       在忽略返回值的上下文裏使用反向引號有何不對?

       嚴格說起來,沒啥不對。但從程式寫作嚴謹與否來說,這樣無法寫出較易維護的程式碼。Perl
       有多種方法可以運行外部命令。反引號只是其中一個;它收集命令的輸出,在程序中加以應用。
       "system" 函數是另一個,它不這樣做

       Writing backticks in your program sends a clear message to the readers of your code that
       you wanted to collect the output of the command.  Why send a clear message that isn't
       true?

       再看看下列這一行:

           `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`;

       在 Perl 5.8.0 中,你可以使用有多個參數的 open()。類似 system()exec()
       的列表形式,不會進行 shell 轉義。

          open( GREP, "-⎪", 'grep', @opts, $search_string, @filenames );
          chomp(@ok = <GREP>);
          close GREP;

       也可以這樣:

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

       一如 system(),當你 exec() 一個序列時不會有 shell 解譯的情況發生。更多示例可以從 perlipc
       的 "Safe Pipe Opens" 中找到。

       Note that if you're use Microsoft, no solution to this vexing issue is even possible.
       Even if Perl were to emulate fork(), you'd still be stuck, because Microsoft does not have
       a argc/argv-style API.

       爲何給了 EOF(Unix上是 ^D,MS-DOS上是 ^Z)後我的程式就不能從 STDIN 讀取東西了呢?

       因爲某些 stdio 的 set error 和 eof 旗標需要清除。你可以用 POSIX 模組裏定
       義的clearerr()。這是在技術上正確的解決之道。還有一些較不保險的方法:

       1   試着保存搜尋指標然後去找它,例如:

               $where = tell(LOG);
               seek(LOG, $where, 0);

       2   如果那樣行不通,試着去 seek() 檔案的另一部份然後再找回來。

       3   如果還是行不通,試着 seek() 檔案另一個相異的的部份,讀點東西,再回去找。

       4   如果依然不行,放棄使用 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;             # new in 5.004
           $handle = IO::Socket::INET->new('www.perl.com:80')
                   ⎪⎪ die "can't connect to port 80 on www.perl.com: $!";
           $handle->autoflush(1);
           if (fork()) {               # XXX: undef means failure
               select($handle);
               print while <STDIN>;    # everything from stdin to socket
           } else {
               print while <$handle>;  # everything from socket to stdout
           }
           close $handle;
           exit;

       如何在 Perl裏達到 Expect的功能?

       很久很久以前,有個叫做 chat2.pl 的程式庫(perl 標準配備之一),但一直沒
       真正完工。如果遇到它的話,不要去用它。現在,你的最佳選擇就是從 CPAN 來的 Expect
       模塊,同時它需要 CPAN 的另兩個模塊, IO::Pty 和 IO::Stty.

       有沒有可能將 perl的指令列隱藏起來,以躲避像

       首先要注意的是,如果你的目的是爲了安全(例如避免人們偷看到密碼),那你應該重寫你的程式,把重要的資訊從參數中剔除。光是隱藏起來不會讓你的程式變得完全安全。

       如要真的把看得見的指令列改掉,你可以設定 $0 這個變數值,如同 perlvar
       裏寫的。但這方法並非各種作業系統都適用。像 sendmail之類的背景程式 (daemons)
       就將它們的狀態放在那兒:

           $0 = "orcus [accepting connections]";

       我在 perl script裏
       {更動目錄,更改我的使用環境}。爲何這些改變在程式執行完後就消失了呢?如何讓我做的修改顯露出來?

       Unix
           嚴格的說起來,這是做不到的-一個 script 的執行是從啓動它的 shell 生出一
           個不同的程序來執行。這個程序的任何變動不會反映到它的父程序,只會反映到更
           改之後它自己創造出來的子程序。有個 shell 魔術可以讓你藉着在 shell 裏 eval()你 script
           的輸出來裝出這種效果,在 comp.unix.questions FAQ 裏有詳 細內容。

       如何關閉一個程序的文件句柄而不用等它完成呢?

       假設你的系統支援這種功能,那就只要送個適當的訊號給此程序(參看 kill)。通常是先送一個 TERM
       訊號,等一下下,然後再送個 KILL 訊號去終結它。

       如何 fork 一個守護進程?

       如果你所指的是離線的程序(未與 tty 連線者),那下列的程序據說在大部份的 Unix系統都能用。非
       Unix 系統的使用者應該檢查 Your_OS::Process 模組看看有 沒有其他的解決方案。

       ·   打開 /dev/tty 然後對它用 TIOCNOTTY ioctl。請參考 tty(4) 。更好的辦法,你可以只用
           POSIX::setsid() 函數,從而不必擔心進程組。

       ·   把目錄換到 /

       ·   重開 STDIN、STDOUT 和 STDERR 使它們不會與舊的 tty 連接。

       ·   用下列方法把程式丟到後臺:

               fork && exit;

       The Proc::Daemon module, available from CPAN, provides a function to perform these actions
       for you.

       如何知道自己是否在交互地運行?

       問得好。有的時候 "-t STDIN"N 和 "-t STDOUT" 可以提供線索,有時不行。

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

       在 POSIX 系統中,你可以用以下方法測試你自己的程序羣組與現在控制你終端機 的是否相同:

           use POSIX qw/getpgrp tcgetpgrp/;
           open(TTY, "/dev/tty") or die $!;
           $tpgrp = tcgetpgrp(fileno(*TTY));
           $pgrp = getpgrp();
           if ($tpgrp == $pgrp) {
               print "foreground\n";
           } else {
               print "background\n";
           }

       如何爲緩慢的事件設置超時?

       如同 Signals 和 Camel 書第六章裏所描述的,用 alarm() 函數,
       或許再配合上一個訊號處理器。你也可以改用 CPAN 裏更具彈性的 Sys::AlarmCall 模組來做。

       The alarm() function is not implemented on all versions of Windows.  Check the
       documentation for your specific version of Perl.

       如何設置 CPU 限額?

       使用 CPAN 裏的 BSD::Resource 模組。

       如何避免在 Unix 系統中產生殭屍進程?

       使用 Signals 裏面叫 reaper 的程式碼,在接到 SIGCHLD 時會呼 叫wait(),或是用 perlfaq8 中的
       "How do I start a process in the background?" 裏面寫的雙 fork 技巧。

       如何使用 SQL 數據庫?

       The DBI module provides an abstract interface to most database servers and types,
       including Oracle, DB2, Sybase, mysql, Postgresql, ODBC, and flat files.  The DBI module
       accesses each database type through a database driver, or DBD.  You can see a complete
       list of available drivers on CPAN: http://www.cpan.org/modules/by-module/DBD/ .  You can
       read more about DBI on http://dbi.perl.org .

       Other modules provide more specific access: Win32::ODBC, Alzabo, iodbc, and others found
       on CPAN Search: http://search.cpan.org .

       如何使 system() 在收到 control-C 時退出?

       做不到。你需要摹仿 system() 呼叫(參看 perlipc
       裏的範例程式),然後設計一個訊號處理器,讓它把 INT 訊號傳給子程序。或者可以檢測它:

           $rc = system($cmd);
           if ($rc & 127) { die "signal death" }

       如何無阻塞地打開一個文件?

       如果你有幸使用到支援無阻塞讀的系統(大部份 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: $!":

       How do I install a module from CPAN?

       最簡單的方法就是讓 CPAN 這個模組替你代勞。這個模組包含在 5.004及以後的版 本中。

           $ perl -MCPAN -e shell

           cpan shell -- CPAN exploration and modules installation (v1.59_54)
           ReadLine support enabled

           cpan> install Some::Module

       如要手動安裝 CPAN 模組,或是任何按規矩發展的 CPAN模組,遵循以下步 驟:

       1   把源代碼解壓到臨時目錄

       2
               perl Makefile.PL

       3
               make

       4
               make test

       5
               make install

       如果你用的 perl 版本在編譯時沒有建入動態連結的功能,那你只消把第叄步 (make)換成 make perl
       然後你就會得到一個新的 perl 執行檔,裏頭連 有你新加入的延伸。

       在 ExtUtils::MakeMaker 裏面有更多關於建構模組的細節,並參考下一個問題,require 和 use
       的區別是什麼?。

       require  use 的區別是什麼?

       Perl offers several different ways to include code from one file into another.  Here are
       the deltas between the various inclusion constructs:

           1)  do $file is like eval `cat $file`, except the former
               1.1: searches @INC and updates %INC.
               1.2: bequeaths an *unrelated* lexical scope on the eval'ed code.

           2)  require $file is like do $file, except the former
               2.1: checks for redundant loading, skipping already loaded files.
               2.2: raises an exception on failure to find, compile, or execute $file.

           3)  require Module is like require "Module.pm", except the former
               3.1: translates each "::" into your system's directory separator.
               3.2: primes the parser to disambiguate class Module as an indirect object.

           4)  use Module is like require Module, except the former
               4.1: loads the module at compile time, not run-time.
               4.2: imports symbols and semantics from that package to the current one.

       In general, you usually want "use" and a proper Perl module.

       如何設置我自己的模塊/庫路徑?

       當你建構模組時,在產生 Makefiles 時使用 PREFIX 選項:

           perl Makefile.PL PREFIX=/mydir/perl LIB=/mydir/perl/lib

       然後在執行用到此 模組/程式庫 的程式前先設好 PERL5LIB 環境變數(參考 perlrun ),或是用

           use lib '/mydir/perl/lib';

       這樣與下面幾乎相同

           BEGIN {
               unshift(@INC, '/mydir/perl/lib');
           }

       但 lib 模塊檢測獨立於機器的子目錄。參見 Perl 的 lib 模塊來獲取詳細信息。

       如何將我自己的程序的路徑加入到模塊/庫搜索路徑中?

           use FindBin;
           use lib "$FindBin::Bin";
           use your_own_modules;

       如何在運行時將一個目錄加入到我的 include 路徑 (@INC) 中?

       以下是我們建議更動引入路徑的方法:

           環境變量 PERLLIB
           環境變量 PERL5LIB
           perl -Idir 命令行標誌
           use lib 編用,類似
               use lib "$ENV{HOME}/myown_perllib";

       後者特別有用,因爲它知道與機器相關的架構。lib.pm 機制模組是從 5.002 版開 始包含在 Perl
       裏面的。

       什麼是 socket.ph,從哪兒可以得到它?

       It's a perl4-style file defining values for system networking constants.  Sometimes it is
       built using h2ph when Perl is installed, but other times it is not.  Modern programs "use
       Socket;" instead.

AUTHOR AND COPYRIGHT

       Copyright (c) 1997-2003 Tom Christiansen and Nathan Torkington.  All rights reserved.

       This documentation is free; you can redistribute it and/or modify it under the same terms
       as Perl itself.

       Irrespective of its distribution, all code examples in this file are hereby placed into
       the public domain.  You are permitted and encouraged to use this code in your own programs
       for fun or for profit as you see fit.  A simple comment in the code giving credit would be
       courteous but is not required.

譯者

       陳彥銘,蕭百齡,兩隻老虎工作室

       

       本頁面中文版由中文 man 手冊頁計劃提供。
       中文 man 手冊頁計劃:https://github.com/man-pages-zh/manpages-zh