2010-04-19 99 views
0

我想用system()命令來運行一些命令,我​​這樣做是這樣的:如何避免在使用system()的Perl中偶然轉義?

execute_command_error("trash-put '/home/$filename'"); 

哪裏,如果有與任何system命令它跑了一個錯誤execute_command_error將報告。我知道我可以使用Perl命令取消該文件的鏈接,但我想使用trash-put刪除東西,因爲它是一種回收程序。

我的問題是,$filename有時會有撇號,引號和其他奇怪的字符,它混淆了system命令或Perl本身。

回答

5

生成命令名和參數作爲數組,並傳遞至系統:

my(@command) = ("trash-put", 'home/$filename'); 
system @command; 

這意味着,Perl不會調用殼做任何元字符膨脹(或I/O重定向,或命令管道或...)。它確實意味着它完全符合你的要求。

sub execute_command_error 
{ 
    system @_; 
} 

從評論的豐富收集借款信息:

這是perldoc -f systemperldoc.perl.org/functions/system.html(@Ether)明確記載。

(參見「EXEC」的討論中緊密相關。)

您的意思是把$文件名中的單引號? (@mobrule)。

我沒打算用單引號 - 我證明了$filename沒有得到通過Perl或殼牌擴大......在我的測試腳本中,我用「my.$file」,這給了我一個文件一個$的名字 - 正如我的意圖。

如果你想調用shell(例如,如果你想要一些管道),我認爲所需的引用是$ command_line =「\」$ command \「\」$ arg1 \「\」$ arg2 \ 「...」。 (@Jefromi)。

添加圍繞論點雙引號不會嵌入$幫助,BACKTICK , '$(...)' 及相關符號。你幾乎需要用單引號括起來,但是你需要重寫嵌入的單引號爲「'\''」,它會生成一個單引號來終止當前的單引號參數,反斜槓引用組合代表一個單引號,而另一個單引號引用來恢復單引號的論點。

如果我直接使用系統命令,這將是一個很好的解決方案;但是我使用的是webmin的execute_command函數,這在我的腦海中有點過分,所以我不知道如何編輯它以允許數組。你能否將嵌入式單引號的重寫擴展爲「'\''」......這就是我現在要用的。(@Brian)

粗略地說,(Unix)shell處理單引號的方式是「從第一個單引號到下一個的所有內容都是文本文本,沒有元字符」。因此,爲了讓shell將某些東西當作文本文本來對待,請將它放在單個字符中。這涉及除單引號之外的所有內容。正如我的評論所說,你必須使用4個字符的替換字符串來將單引號嵌入單引號參數的中間。

有可能是一個更合適的方法來做到這一點比這(使用一個或兩個map操作,也許),但這應該工作:

for (my $i = 0; $i < scalar(@command); $i++) 
{ 
    $command[$i] =~ s/'/'\\''/g; # Replace single quotes by the magic sequence 
    $command[$i] = "'$command[$i]'"; # Wrap value in single quotes 
} 

然後,您可以加入數組做一個字符串傳輸到execute_command


這是更好地寫,作爲系統{$命令[0]} @command來處理的情況下@command有一個元素。這是我在掌握Perl的「安全編程技術」一章中討論的內容之一。 (@briandfoy)。

作爲一般規則,我會接受此更正。不過,在這種情況下,我不確定它是否至關重要,其中命令名是由程序提供的,它只是可能提供給用戶的參數。命令名'trash-put'對於shell擴展是安全的(IFS在啓動時被shell重置爲默認值,因此攻擊途徑不可用)。

這個問題在'perldoc -f exec'手冊頁討論:

如果你真的不想要執行的第一個參數,但要騙你正在執行有關自己的名字的程序,你可以在LIST前面指定您實際想要作爲「間接對象」(不帶逗號)運行的程序。 (這總是迫使LIST的解釋作爲多值列表中,即使僅在列表中的單個標量。)

實施例:

$shell = '/bin/csh'; 
    exec $shell '-sh'; # pretend it's a login shell 

,或者更直接地,

exec {'/bin/csh'} '-sh'; # pretend it's a login shell 

當參數獲得通過系統shell執行結果受其怪癖和能力的影響。有關詳細信息,請參閱perlop中的「STRING」。

對exec或system使用間接對象也更安全。這種用法(對於system()也可以正常工作)強制將參數解釋爲多值列表,即使列表只有一個參數。這樣你就可以安全地從shell擴展通配符,或者用空格分開單詞。

@args = ("echo surprise"); 
    exec @args;    # subject to shell escapes 
          # if @args == 1 
    exec { $args[0] } @args; # safe even with one-arg list 

第一個版本中,一個沒有間接對象,跑到回波程序,向它傳遞「驚喜」的參數。第二個版本沒有;它試圖運行一個名爲「echo surprise」的程序,沒有找到它,並設置$?指示失敗的非零值。


你怎麼背打勾在降價中顯示?

+2

......在'perldoc -f system'中清楚地記錄了這一點:http://perldoc.perl.org/functions/system.html – Ether 2010-04-19 21:58:00

+0

你是否想把'$ filename'放在單引號中? – mob 2010-04-19 22:00:09

+0

我認爲所需的引用,如果你想要調用shell(例如,如果你想要一些管道)是'$ command_line =「\」$ command \「\」$ arg1 \「\」$ arg2 \「... 「'。 – Cascabel 2010-04-19 22:00:40

2

perldoc -f system指出:

如果在列表中有多個參數,或如果LIST是一個數組 與多於一個的值,開始由上述列表的與第一元件中給出的程序列表中其餘部分給出的論點。 如果有 只有一個標量參數,則檢查參數是否有shell元字符,如果有,則將整個參數傳遞到系統的 命令行解析器(這是「/ bin/sh -c」) Unix平臺,但在其他平臺上有所不同)。如果 參數中沒有shell元字符,則將其分割爲單詞並直接傳遞給「execvp」,這更有效。

我喜歡用的IPC::System::Simple版本系統(),它提供了更多的控制,例如能夠捕獲各種異常和處理某些錯誤代碼爲‘壞’等,作爲‘好’:

use IPC::System::Simple qw(system); 
system("cat *.txt"); # will die on failure 
相關問題