程式上偶爾會需要取用到系統或其它非PERL本身的程式,PERL該如何呼叫?
PERL提供 system, exec, 反單引號` 等函數可供叫用,以下分別來說明。
呼叫外部程式
使用system來執行外部程式
假如在perl中想要呼叫其它的程式,稱之為「外部程式」,可以直接使用 system這個函數,例如 pwd顯示目錄的目錄及ls傾印:
system("pwd");
system("ls");
輸出結果就和我們平時下指令沒什麼差別。
要注意的是,每一個system指令都是獨立的,並不是在同一個狀態(session)之下,舉個例子來說,我們希望傾印此目錄所在位置的上一層目錄,如果寫成這樣:
system("ls");
看起來有先切換到上一層目錄再執行傾印檔案,結果卻還是印出目前的目錄下的檔案,並不會印出上一層的檔案,這顯然不是我們想像的結果。
當然,解決的方法並不難,例如可以把指定寫在同一行,並用分號來分隔;或是指定目錄來傾印:
system("ls ../");
如果指令中帶有引數,也可以使用陣列的方法傳入:
system(@args) == 0 or die "system @args failed: $?"
以上 $? 是標準錯誤(STDERR)回傳的結果。
system的執行方式是執行後並等待回應,換句話說,假設叫用的外部程式有狀況,程式也只能等待,
正常的情況下,當叫用的外部程式結束後,會回到perl的行程並往下執行。
使用exec來執行外部程式
exec 和system 類似,只是exec不會回傳結果。
但是和system一樣,假如呼叫的指令本身就具有傾印結果的作用,則不在此限,例如執行以下的指令:
exec("ls");
exec("date");
exec("echo 'SOME TEXT'");
簡單來說,system執行時,原本的 Perl 程式會待在哪裡沒事做等待 system 裡執行的指令結束。但是exec 執行時,
原本Perl程式後面的行程就已經不存在了,只剩下執行 exec中命令的行程,而且該行程執行完畢之後,也不再返回原本的 Perl 行程。
例如下面的程式:
exec("date"); print "After exec\n";
執行結果
<<'After exec'這行不會被印出來,同時perl已經結束。>>
使用單引號來執行外部程式(backticks)
單引號的行為就像是直接shell的叫用方法
上面的引號是反單引號 ` ,鍵盤上數字1左邊那個按鍵,英文叫作backticks,千萬不要用錯。
反單引號是將送到標準輸出(STDOUT)的結果當回傳值,換句話說,原本要送到標準輸出的結果就會被劫斷,例如本來要印出日期時間的指令date:
print `date`; <== 印出日期時間
反單引號也可以使用 qx() 來代替,例如以下兩種是等義:
qx(date);
取回叫用程式的執行結果
實務上叫用系統程式,有時需要取回執行的結果,我們先寫程式觀察上面的這三種方法再作結論:
$a=`date`; print "單引號:$a\n"; $b= system("date"); print "system函數:$b\n"; $c= exec("date"); print "exec函數:$c\n";
執行結果
單引號:2021年11月 9日 週二 11時49分44秒 CST
2021年11月 9日 週二 11時49分44秒 CST
system函數:0
2021年11月 9日 週二 11時49分44秒 CST
第8行 因為exec後面的部分不會被執行,所以第8行不會顯示
由此可以看到只有反單引號可以正確的將傾印的結果傳給變數$a,system回傳一個0(此例回傳空值),而exec執行完即結束。
因此使用反單引號正是我們需要的。
評估 eval 的使用
PERL 提供了一個評估(eval)的方法,從字面上很難理解他的意義,簡單來說,eval就像是一個沙盒(sandbox)或是perl的虛擬機,我們可以在沙盒中執行某個函數或是程式區塊。
假設在這裡出了錯,PERL並不會因為錯誤而中止。
舉例來說,以下是一個一定會出錯的式子,因為除以0不被計算機接受。
執行結果
Illegal division by zero at 01.pl line X
就算出錯,如果我們也希望程式不要因此報錯或是不要中止的話,可以使用評估的方法,把原本的指令放在雙引號中,再交給eval執行。:
執行結果
<<沒有任何輸出也不會報錯>>
但是如此,會不知道在eval裡面到底發生了什麼事,還好有錯誤的話,eval會把錯誤寫到變數 $@ 中,只要判斷 $@是否為空字串即可。
print $@;
執行結果
Illegal division by zero at (eval 1) line 1.
一般而言,只要判斷 $@是否為空字串,否則就印出警告,只要評估中沒有錯誤, $@ 一定是空字串。
warn $@ if $@;
評估可以不只寫成一行,也可以用大括號來包圍一個區塊,最後的分號很常被漏掉
例如下面的範例,在eval 中的變數能夠被eval外面讀到
eval { $a=123; $b=0; print $a/$b; }; print $a;
執行結果
上面的範例可以發現第4行錯誤的計算並沒有顯示,但也沒有報錯,這就是評估好用的地方,類似在其它程式語言中的 try...catch 語法。
eval 還有其它作用,留待有興趣的人自行研究。
結論
1. 呼叫外部程式可用 system、exec、backticks 等內建的方式。
2. 呼叫外部程式並等待使用 system,呼叫外部程式後原perl程序就不執行用exec,需要取得執行結果用 backticks。
3. eval可以讓指定放在沙盒中執行,不因錯誤而停止。
參考資料
[1] https://stackoverflow.com/questions/799968/whats-the-difference-between-perls-backticks-system-and-exec
[2] http://puremonkey2010.blogspot.com/2010/09/perl-ch16.html
上一篇 23-多執行緒
回到目錄 01-撰寫第一隻PERL程式/目錄
下一篇 25-通訊和網路