IPC::Cmd - 找到並執行系統命令變得容易
use IPC::Cmd qw[can_run run run_forked];
my $full_path = can_run('wget') or warn 'wget is not installed!';
### commands can be arrayrefs or strings ###
my $cmd = "$full_path -b theregister.co.uk";
my $cmd = [$full_path, '-b', 'theregister.co.uk'];
### in scalar context ###
my $buffer;
if( scalar run( command => $cmd,
verbose => 0,
buffer => \$buffer,
timeout => 20 )
) {
print "fetched webpage successfully: $buffer\n";
}
### in list context ###
my( $success, $error_message, $full_buf, $stdout_buf, $stderr_buf ) =
run( command => $cmd, verbose => 0 );
if( $success ) {
print "this is what the command printed:\n";
print join "", @$full_buf;
}
### run_forked example ###
my $result = run_forked("$full_path -q -O - theregister.co.uk", {'timeout' => 20});
if ($result->{'exit_code'} eq 0 && !$result->{'timeout'}) {
print "this is what wget returned:\n";
print $result->{'stdout'};
}
### check for features
print "IPC::Open3 available: " . IPC::Cmd->can_use_ipc_open3;
print "IPC::Run available: " . IPC::Cmd->can_use_ipc_run;
print "Can capture buffer: " . IPC::Cmd->can_capture_buffer;
### don't have IPC::Cmd be verbose, ie don't print to stdout or
### stderr when running commands -- default is '0'
$IPC::Cmd::VERBOSE = 0;
IPC::Cmd 允許您在不同平台上運行命令,如果需要,可以互動式地運行,但仍然能夠正常工作。
can_run
函式可以告訴您某個特定二進制文件是否已安裝,如果已安裝,則可以告訴您其所在位置;而 run
函式則可以實際執行您給出的任何命令,並給您清晰的返回值,同時遵循您的冗長設置。
這是一個實用函式,告訴您是否可使用 IPC::Run
。如果傳遞了 verbose
標誌,則在無法找到或加載 IPC::Run 時,它將打印診斷消息。
這是一個實用函式,告訴您是否可使用 IPC::Open3
。如果傳遞了 verbose 標誌,則在無法找到或加載 IPC::Open3
時,它將打印診斷消息。
這是一個實用函式,告訴您 IPC::Cmd
是否能夠在其當前配置中捕獲緩衝區。
這是一個實用函式,告訴您 IPC::Cmd
是否能夠在當前平台上提供 run_forked
。
can_run
函式僅接受一個參數:您希望定位的二進制文件的名稱。 can_run
的工作方式類似於 unix 二進制文件 which
或 bash 命令 type
,它通過您的路徑掃描,尋找所需的二進制文件。
與 which
和 type
不同,此函式是跨平台的,例如,在 Win32 上也可以使用。
如果以純量上下文調用它,則如果找到了您要求的二進制文件,它將返回該二進制文件的完整路徑,如果未找到,則返回 undef
。
如果以列表上下文調用,且全局變量 $INSTANCES
是真值,則它將返回在 PATH
中找到的二進制文件的實例的完整路徑列表,如果未找到,則返回空列表。
run
函數接受 4 個參數
這是要執行的命令。它可以是字符串或數組引用。這是一個必需的參數。
關於命令如何解析及其限制的備註,請參見 "注意事項"。
這控制是否還要將命令的所有輸出打印到 STDOUT/STDERR,或者只應該將其捕獲在緩衝區中(注意:緩衝區需要安裝 IPC::Run,或者您的系統能夠使用 IPC::Open3)。
它將默認為全局設置的 $IPC::Cmd::VERBOSE
,默認值為 0。
這將保存命令的所有輸出。它需要是一個標量的引用。請注意,這將保存 STDOUT 和 STDERR 消息,您無法知道哪個是哪個。如果需要區分這一點,請以列表上下文運行 run
命令並檢查各個緩衝區。
當然,這需要底層調用支持緩衝區。請參見上面關於緩衝區的注釋。
設置命令允許運行的最大時間,使用內置的 alarm()
調用進行中止。如果超時觸發,返回值中的 errorcode
將設置為 IPC::Cmd::TimeOut
類的對象。有關詳細信息,請參見下面的 "錯誤消息" 部分。
默認為 0
,表示未設置超時。
run
在標量上下文中調用時將返回一個簡單的 true
或 false
。在列表上下文中,將返回以下項目的列表
一個簡單的布爾值,指示命令是否沒有錯誤地執行。
如果返回值的第一個元素(success
)為0,則表示發生了一些錯誤。第二個元素是你所請求的命令退出時的錯誤訊息,如果有的話。這通常是$?
或$@
的美化值。詳細內容請參閱perldoc perlvar
。如果錯誤是超時,error message
將以字符串IPC::Cmd::TimeOut
作為前綴,這是超時的類別。
這是一個包含命令產生的所有輸出的陣列參考。請注意,只有在安裝了IPC::Run或者系統能夠使用IPC::Open3時,才會有這些緩衝區,否則該元素將為undef
。
這是一個包含所有發送到標準輸出的命令產生的輸出的陣列參考。與"full_buffer"中的注意事項相同。
這是一個包含所有發送到標準錯誤的命令產生的輸出的陣列參考。與"full_buffer"中的注意事項相同。
查看下面的"HOW IT WORKS"部分,了解IPC::Cmd
在發出命令時如何決定使用哪些模組或函數呼叫。
run_forked
用於執行某個程序或代碼引用,可選擇性地向其提供一些輸入,獲取其返回碼和輸出(分別為stdout和stderr),此外,它允許在程序花費太長時間完成時終止該程序。
run_forked
的重要和獨特特點是執行超時,在一開始看起來這似乎是一個相當簡單的任務,但如果你認為你要啟動的程序可能會啟動一些子程序(它們又可能會做同樣的事情,以此類推),這就不是一個簡單的問題。
run_forked
被設計為能夠在幾乎任何長時間運行的任務中存活並成功終止,即使在給定的超時期間內出現fork bomb,如果您的系統有資源可以在給定的超時期間內存活下來。
這是通過創建單獨的看門狗進程來實現的,該進程在單獨的進程會話中生成指定的程序並監視它:可選地向其提供輸入,存儲其退出代碼、stdout 和 stderr,如果運行時間超過指定時間,則終止它。
調用需要執行的命令或 coderef,可選地是選項的哈希引用
timeout
在秒中指定命令運行多長時間後,它將被用 SIG_KILL (9) 殺死,這將有效地終止它及其所有子進程(直接或間接)。
child_stdin
指定要傳遞給執行程序的 STDIN
的一些文本。
stdout_handler
從執行程序的 STDOUT 收到一部分數據時調用的子例程的 coderef。
stderr_handler
從執行程序的 STDERR 收到一部分數據時調用的子例程的 coderef。
wait_loop_callback
主等待循環內部調用的子例程的 coderef(當 run_forked
等待外部進程完成或失敗時)。在外部命令基於其輸出終止之前,停止運行外部進程很有用,例如。
my $r = run_forked("some external command", {
'wait_loop_callback' => sub {
if (condition) {
kill(1, $$);
}
},
'terminate_on_signal' => 'HUP',
});
結合 stdout_handler
和 stderr_handler
允許根據其輸出終止外部命令。也可以作為計時器使用,而不需要使用 alarm(信號)。
請記住,此代碼可能每毫秒被調用一次(取決於外部命令生成的輸出),因此請儘可能使其輕量級。
discard_output
丟棄 run_forked() 返回的標準輸出和標準錯誤的緩衝區。使用此選項,您必須使用 std*_handlers 讀取命令的輸出。對於發送大量輸出的命令很有用。
terminate_on_parent_sudden_death
啟用此選項,如果最初生成的進程(父進程)被殺死或意外死亡,則希望終止所有衍生的進程,而不必等待子進程。
run_forked
將返回具有以下鍵的 HASHREF
exit_code
執行程序的退出代碼。
timeout
程序在被終止之前運行的秒數,如果沒有超時則為 0。
stdout
保存執行命令的標準輸出(如果沒有標準輸出或使用discard_output
時為空字符串;它始終被定義!)
stderr
保存執行命令的標準錯誤(如果沒有標準錯誤或使用discard_output
時為空字符串;它始終被定義!)
merged
將執行命令的標準輸出和錯誤合併為一個流(如果根本沒有輸出或使用discard_output
時為空字符串;它始終被定義!)
err_msg
在出現錯誤時保存一些說明。
返回在此平台上用於引用字符串的字符。在大多數系統上,這通常是'
(單引號),但某些系統使用不同的引號。例如,Win32
使用"
(雙引號)。
您可以像下面這樣使用它
use IPC::Cmd qw[run QUOTE];
my $cmd = q[echo ] . QUOTE . q[foo bar] . QUOTE;
這確保foo bar
被視為一個字符串,而不是echo
函數的兩個獨立參數。
run
將嘗試使用以下邏輯執行您的命令
如果已安裝IPC::Run
並且變量$IPC::Cmd::USE_IPC_RUN
設置為true(參見"全局變量"部分),則使用它來執行命令。您將在緩衝區中有完整的輸出,互動命令肯定可以正常工作,並且保證整潔地保留您的冗長設置。
否則,如果將變量$IPC::Cmd::USE_IPC_OPEN3
設置為true(參見"全局變量"部分),則嘗試使用IPC::Open3執行命令。在所有平台上都將有緩衝區可用,互動命令仍將正常執行,而且您的冗長設置也將被很好地遵守;
否則,如果將verbose
參數設置為true,我們將回退到簡單的system()
調用。我們無法捕獲任何緩衝區,但互動命令仍然可以工作。
否則,我們將嘗試暫時重定向STDERR和STDOUT,使用您的命令執行system()
調用,然後重新打開STDERR和STDOUT。這是最後一個手段的方法,仍然可以讓您整潔地執行命令。但是,將不可用任何緩衝區。
IPC::Cmd的行為可以通過更改以下全局變量來修改
這個控制 IPC::Cmd 是否將來自命令的任何輸出列印到螢幕上。默認值為 0。
此變數控制當可用且適用時,IPC::Cmd 是否嘗試使用 IPC::Run。
此變數控制當可用且適用時,IPC::Cmd 是否嘗試使用 IPC::Open3。默認值為 true。
此變數控制是否應該發出運行時警告,例如明確要求的 IPC::* 模塊無法加載的失敗。
默認為 true。關閉此功能需自擔風險。
此變數控制當以列表上下文調用時,can_run 是否返回路徑中找到的二進制文件的所有實例。
默認為 false,設為 true 以啟用所描述的行為。
此變數控制 run 是否會刪除在命令參數中找到的任何空/空值參數。
默認為 false,因此將刪除空參數。設為 true 以允許它們。
當使用 IPC::Open3 或 system 時,如果將字符串作為命令參數提供,則假定它已適當逃脫。您可以使用 QUOTE 常量作為可移植的引號字符(請參見上文)。但是,如果提供數組引用,則適用特殊規則。
如果您的命令包含特殊字符(< > | &),則在執行命令之前將其內部化,以避免這些特殊字符被逃脫並作為參數傳遞而不是保留其特殊含義。
但是,如果命令包含包含空格的參數,將命令內部化將失去空格的意義。因此,如果命令以數組引用形式傳遞且包含特殊字符,IPC::Cmd 將對命令中包含空格的任何參數進行引用。
當使用 IPC::Run
時,如果您將字符串作為 command
參數提供,則該字符串將根據空格進行拆分,以確定命令的各個元素。儘管這通常只會做您想做的事,但如果您的文件或命令中包含空格,則可能會出現問題。
如果您不希望發生這種情況,您應該提供一個數組引用,其中您命令的所有部分已經分開。但是請注意,如果這些部分中有額外或多餘的空格,則解析器或底層代碼可能無法正確解釋它,並且可能會導致錯誤。
例子:以下代碼
gzip -cdf foo.tar.gz | tar -xf -
應該傳遞為
"gzip -cdf foo.tar.gz | tar -xf -"
或者為
['gzip', '-cdf', 'foo.tar.gz', '|', 'tar', '-xf', '-']
但要注意不要將其傳遞為,例如
['gzip -cdf foo.tar.gz', '|', 'tar -xf -']
因為這將導致如上述描述的問題。
目前解析命令中的 IO 重定向太複雜了。但是,對於捕獲 STDOUT 或 STDERR,有一個解決方法,因為您可以檢查您的緩衝區中的內容。
無論是 IPC::Run 還是 IPC::Open3 都無法交錯 STDOUT 和 STDERR。對於程序的短暫輸出突發情況,例如這個示例,
for ( 1..4 ) {
$_ % 2 ? print STDOUT $_ : print STDERR $_;
}
IPC::[Run|Open3] 首先將讀取所有的 STDOUT,然後讀取所有的 STDERR,這意味著輸出看起來像是 STDOUT 上的 '13',STDERR 上的 '24',而不是
1
2
3
4
這已經在 rt.cpan.org 記錄為 bug #37532: 無法交錯 STDOUT 和 STDERR。
感謝 James Mastros 和 Martijn van der Streek 幫助使 IPC::Open3 表現得很好。
感謝 Petya Kohts 提供 run_forked
代碼。
請將錯誤或其他問題報告給 <bug-ipc-cmd@rt.cpan.org>。
原作者:Jos Boumans <kane@cpan.org>。目前的維護者:Chris Williams <bingos@cpan.org>。
此程式庫屬於自由軟體,您可以按照 Perl 本身的相同條款進行重新分發和/或修改。