關於perl中的反勾號(``),system和exec

關於perl中的反勾號(``),systemexec

 

perl中反勾號(``),systemexec都用來執行命令,這篇文章將給我們介紹它們各自的使用方法,聯繫,以及區別。

 

一、使用方法

1.       反勾號(``

首先,我們有命令輸入操作符,也叫反勾號操作符,因爲它看起來象這樣:

$info = `finger $user`;

一個用反勾號(技術上叫重音號)引起的字串首先進行變量替換,就象一個雙引號引起的字串一樣。得到的結果然後被系統當作一個命令行,而且那個命令的輸出成爲僞文本的值。(這是一個類似 Unix shell 的模塊。)在標量環境裏,返回一個包含所有輸出的字串。在列表環境裏,返回一列值,每行輸出一個值。(你可以通過設置 $/ 來使用不同的行結束符。)

每次計算僞文本的時候,該命令都得以執行。該命令的數字狀態值保存在 $?(參閱第二十八章獲取 $? 的解釋,也被稱爲 $CHILD_ERROR )。和這條命令的 csh 版本不同的是,對返回數據不做任何轉換——換行符仍然是換行符。和所有 shell 不同的是,Perl 裏的單引號不會隱藏命令行上的變量,使之避免代換。要給 shell 傳遞一個 $,你必須用反斜槓把它隱藏起來。我們上面的 finger 例子裏的 $user Perl 代換,而不是被 shell。(因爲該命令 shell 處理,參閱第二十三章,安全,看看與安全有關的內容。)

反勾號的一般形式是 qx//(意思是引起的執行),但這個操作符的作用完全和普通的反勾號一樣。你只要選擇你的引起字符就行了。有一點和引起的僞函數類似:如果你碰巧選擇了單引號做你的分隔符,那命令行就不會進行雙引號代換;

$perl_info = qx(ps $$); # 這裏 $$ Perl 的處理對象 $perl_info = qx'ps $$'; # 這裏 $$ shell 的處理對象

下面是一個例子:

在筆者的F盤中存在一個perl文件F://Demo3.pls,它的作用就是被另外一個程序F://Demo1.pls調用,然後F://Demo3.pls讀取log3.log中的數據。

 

Demo3.pls

#!/usr/bin/perl -w

 

use strict;

use warnings;

 

unless(open(FILE_H,"<F://log3.log")){

    print "Can not open the file";

}

 

my @str = <FILE_H>;

 

my $count = @str;

close(FILE_H);

 

for(my $i = 0;$i<$count;$i++){

   print "$str[$i]";

}

Demo.pls

#!/usr/bin/perl –w

my @str = qx/perl F://Demo3.pls/;#或者` perl F://Demo3.pls `效果#一樣

print "@str";

Log3.log

首先,我們有命令輸入操作符,也叫反勾號操作符,因爲它看起來象這樣:

 

$info = `finger $user`;

 

一個用反勾號(技術上叫重音號)引起的字串首先進行變量替換,就象一個雙引號引起的字串一樣。得到的結果然後被系統當作一個命令行,而且那個命令的輸出成爲僞文本的值。(這是一個類似 Unix shell 的模塊。)在標量環境裏,返回一個包含所有輸出的字串。在列表環境裏,返回一列值,每行輸出一個值。(你可以通過設置 $/ 來使用不同的行結束符。)

 

代碼解釋,當執行Demo.pls時,my @str = qx/perl F://Demo3.pls/;這一句會被操作系統調用並且啓動Demo3.pls,然後Demo3.pls會讀取log3.log中的數據。

 

我們主要關注的是my @str = qx/perl F://Demo3.pls/;這句中的返回值@str,我們都知道@str是一個列表環境,反勾號返回Demo3.pls中的print的打印值,當然我們也可以使用標量環境,例如,my $str = qx/perl F://Demo3.pls/;這樣也可以一次性的取出所有的數據。

 

最後總結:反勾號返回的是命令行返回的print的數值。具體的解釋看上面的說明。

 

2.       System

l  system PATHNAME LIST

l  system LIST

這個函數爲你執行任何系統裏的程序並返回該程序的退出狀態——而不是它的輸出。要捕獲命令行上的輸出,你應該用反勾號或者 qx//system 函數的運轉非常類似 exec,只不過 system 先做一個 fork,然後在 exec 之後等待執行的程序的結束。也就是說它爲你運行這個程序並且在它完成之後返回,而 exec 用新的程序代替你運行的程序,所以如果替換成功的話它從不返回。

參數的處理因參數的數目的不同而不同,就象在 exec 裏描述的那樣,包括判斷是否調用 shell 以及你是否用聲明另外一個 PATHNAME 的方法使用了該函數其他的名稱。

因爲 system 和反勾號阻塞 SIGINT SIGQUIT,所以向那些正在這樣運行的程序發送這些信號之一(比如通過一個 Control-C)時並不會中斷你的主程序。但是你運行的另外一個程序的確收到這個信號。請檢查 system 的返回值,判斷你運行的程序是否正常退出。

   @args = ("command", "arg1", "arg2");

   system(@args) == 0

      or die "system @args failed: $?"

返回值是和該函數通過 wait(2) 系統調用返回的一樣的退出狀態。在傳統的語意裏,要獲取實際的退出值,要除以 256 或者右移 8 位。這是因爲低 8 位裏有一些其他的東西。(實際上是其他的兩些東西。)最低七位標識殺死該進程的信號號碼(如果有的話),而第八位標識該進程是否傾倒了核心。你可以通過 $?$CHILD_ERROR)來檢查所有失效可能性,包括信號和核心傾倒:

   $exit_value = $? >> 8;

   $exit_value = $? & 127;   # 或者 0x7f, 0177, 0b0111_1111

   $dumped_core = $? & 128;   #  或者 0x80, 0200, 0b1000_0000

如果該程序是通過系統 shell (注:定義爲 /bin/sh 或者任何在你的平臺上有意義的東西,但不是那些用戶碰巧在某個時候用到的 shell。)運行的,這可能是因爲你只有一個參數而且該參數裏面有 shell 元字符,那麼通常返回碼受那個 shell 的怪癖和功能的影響。換句話說,在這種情況下,你可能無法獲取我們前面描述了詳細信息。

 

3.       exec 

o    exec PATHNAME LIST

o    exec LIST

exec 函數結束當前程序的運行並且執行一條外部命令並且決不返回!!!如果你希望在該命令退出之後恢復控制,那麼你應該使用 systemexec 函數只有在該命令不存在以及該命令是直接執行而沒有通過你的系統的命令行 shell(下面討論)執行的時候才失敗並返回假。

如果只有一個標量參數,那麼 exec 檢查該參數是否 shell 的元字符。如果找到元字符,那麼它代表的所有參數都傳遞給系統的標準命令行解釋器(在 Unix 裏是 /bin/sh)。如果沒有這樣的元字符,那麼該參數被分裂成單詞然後直接執行,出於效率考慮,這樣做繞開了所有 shell 處理的過荷。而且如果該程序沒有退出,這樣也給你更多錯誤恢復的控制。

如果在 LIST 裏有多於一個參數,或者如果 LIST 是一個帶有超過一個值的數組,那麼就決不會使用系統的 shell。這樣也繞開了 shell 對該命令的處理。在參數中是否出現元字符並不影響這個列表觸發特性,這麼做也是有安全考慮的程序的比較好的做法,因爲它不會把自己暴露在潛在的 shell 逃逸之中。

下面的例子令當前運行的 Perl 程序用 echo 程序代替自身,然後它就打印出當前的參數列表:

   exec 'echo', 'Your arguments are: ', @ARGV;

下面這個例子顯示了你可以 exec 一個流水線,而不僅僅是一個程序:

   exec "sort $outfile | uniq"

      or die "Can't do sort/uniq: $!/n";

通常,exec 從不返回——就算它返回了,它也總是返回假,並且你應該檢查 $! 找出什麼東西出錯了。要注意的是,在老版本的 Perl 裏,exec(和 system)並不刷新你的輸出緩衝,所以你需要在一個或更多個文件句柄上通過設置 $| 打開命令緩衝功能以避免在 exec 的情況下丟失輸出,或者在 system 的情況下打亂了輸出順序。在 Perl 5.6 裏情況大致如此。

如果你讓操作系統在一個現有的進程裏運行一個新的程序(比如 Perl exec 函數做的這樣),你要告訴系統要執行的程序在哪裏,但是你也告訴了這個新的程序(通過它的第一個參數)是什麼程序執行了它。習慣上,你告訴它的名字只是該程序的位置的一個拷貝,但這麼做不是必須的,因爲在 C 語言的級別上,有兩個獨立的參數。如果這個名字不是拷貝,那麼你就可能看到奇怪的結果:這個新程序認爲自己是以一個和它所在的實際路徑名完全不同的名字運行的。通常這樣對那些滿腹狐疑的程序來說沒什麼問題,但有些程序的確關心自己的名字,並且根據自己的名字的變化會有不同的性格。比如,vi 編輯器會看看自己是作爲“vi”還是作爲“view”調用的。如果作爲“view”,那麼它就自動打開只讀模式,就好象它是帶着 -R 命令行選項調用的一樣。

這個時候就是 exec 的可選 PATHNAME 參數起作用的地方了。從語意上來看,它放在間接對象的位置,就好象 print printf 的文件句柄一樣。因此,它並不需要在後面有一個對象,因爲它實際上不是參數列表的一部分。(從某種意義上來說,Perl 與操作系統採取的方法正相反,它認爲第一個參數是最重要的,並且如果它不同那麼就讓你修改路徑名。)比如:

    $editor = "/usr/bin/vi";

   exec $editor "view", @files   # 觸發只讀模式

      or die "Couldn't execute $editor: $!/n";

和任何其他間接對象一樣,你也可以用一個包含任意代碼的塊代替上面這個保存程序名的簡單標量,這樣就可以把前面這個例子簡化爲:

   exec { "/usr/bin/vi" } "view" @files      # 觸發只讀模式

      or die "Couldn't execute $editor: $!/n";

如前所述,exec 把一個離散的參數列表當作一個它應該繞開 shell 處理的標誌。不過,仍然有一個地方可能把你拌倒。exec 調用(以及 system)不能區別單個標量參數和一個只有一個元素的列表。

   @args = ("echo surprise")   # 只有一個元素在列表裏

   exec @args         # 仍然可能有 shell 逃逸

      or die "exec: $!";   # 因爲 @args == 1

爲了避免這種情況,你可以使用 PATHNAME 語法,明確地把第一個參數當路徑名複製,這樣就強制其他的參數解釋成一個列表,即使實際上只有一個元素:

    exec { $args[0] } @args   # 就算是隻有一個元素的列表也安全了

      or die "can't exec @args: $!";

第一個沒有花括弧的版本,運行 echo 程序,給它傳遞“surprise”做參數。第二個版本不是這樣——它試圖運行一個字面上叫 echo surprise 的程序,但找不到(我們希望如此),然後把 $! 設置爲一個非零值以表示失敗。

因爲 exec 函數通常是緊跟在 fork 之後調用的,所以它假定任何原先一個 Perl 進程終止的時候要發生的事情都被忽略。在 exec 的時候,Perl 不會調用你的 END 塊,也不會調用與任何對象相關的 DESTROY 方法。否則,你的子進程結束的時候會做那些你準備在父進程裏做的清理工作。(我們希望在現實生活中就是如此。)

因爲把 exec 當作 system 用是一個非常普遍的錯誤,所以如果你帶着流行的 -w 命令行開關運行,或者你用了 use warnings qw(exec syntax) 用法的時候,如果 exec 後面跟着的語句不是 diewarn,或則 exit,那麼 Perl 就會警告你。如果你真的想在 exec 後面跟一些其他的語句,你可以使用下面兩種風格之一以避免警告:

   exec ('foo)   or print STDERR "couldn't exec foo: $!";

   { exec ('foo') };       print STDERR "couldn't exec foo: $!";

正如上面的第二行顯示的那樣,如果調用 exec 的時候是一個塊裏的最後一條語句,那麼就可以免於警告。

又見 system

 

發佈了48 篇原創文章 · 獲贊 2 · 訪問量 22萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章