Exec
Kernel#exec
通過執行給定的命令來替換當前進程,例如:
$ irb>> exec ‘echo "hello $HOSTNAME"‘hello codefun$
注意 exec
利用 echo
命令替換了 irb
進程,然後退出。因為 Ruby 實際上結束了該方法,所以只能有限使用。該方法的缺點是,你無法從 Ruby 指令碼中知道命令是執行成功還是失敗。
System
system
命令與 exec
操作相似,但它是在 subshell 中執行,而不是替換當前進程。與 exec
相比,system
給我們更多的資訊。如果命令執行成功,它返回 true;否則返回 false。
$irb>> system ‘echo "hello $HOSTNAME"‘ hello codefun=> true>> system ‘false‘=> false>> puts $?256=> nil>>
system
將進程的退出狀態設定到全域變數 $?
。注意 false
命令的退出狀態,總是非 0 值。檢查退出碼讓我們既可以拋出(raise)異常,又能夠重試(retry)命令。
新手注意:Unix 命令執行成功退出碼為 0,否則為非 0。
如果我們想知道“命令是否執行成功呢?”,使用 system
就很好。然而,我們時常想要捕獲命令的輸出,並在程式中使用。
Backticks (`)
backticks(也叫“backquotes”)在 subshell 中執行命令,並從該命令返回標準輸出。
$ irb>> today = `date`=> "Wed Jul 4 22:03:15 CST 2012\n">> $?=> #<Process::Status: pid 6169 exit 0>>> $?.to_i=> 0
這也許是在 subshell 中執行命令最廣為人知的方法。如你所見,它返回了命令的輸出,然後我們可以像其他字串一樣使用它。
注意 $?
並非是返回狀態的整數,而實際是 Process::Status 對象。我們不僅得到了退出狀態,而且有進程 ID。Process::Status#to_i
返回整數型的退出狀態(#to_s
返回字串型的退出狀態)。
使用 backticks 我們只能獲得命令的標準輸出(stdout),而不能獲得其標準錯 誤(stderr)。在下面的例子中,我們執行 Perl 指令碼來輸出字串到標準錯誤。
$ irb>> warning = `perl -e "warn ‘dust in the wind‘"`dust in the wind at -e line 1.=> "">> puts warning=> nil
注意變數 warning 沒有被設定。當我們在 Perl 中執行 warn
時,產生的標準錯誤輸出並沒有被 backticks 捕獲。
IO#popen
IO#popen
是在子進程中執行命令的另一種方法。popen
給你更多的控制,子進程的標準輸入和標準輸出都會串連到 IO 對象。
$ irb>> IO.popen("date") { |f| puts f.gets }Wed Jul 4 22:02:31 CST 2012=> nil
雖然 IO#popen
不錯,但是當需要這類細分層次的資訊時,我通常使用Open3#popen3
。
Open3#popen3
Ruby 標準庫包含 Open3 類。它易用,並能返回標準輸入、標準輸出、以及標準 錯誤。在本例中,讓我們使用互動命令 dc
。dc 是從標準輸入讀取的逆波計算機(reverse-polish calculator)。我們先 push 兩個數字和一個操作符到堆棧 中。然後我們使用 p
來列印輸出結果。下面我們 push 5、10 及 +,結果標準輸出獲得了15。
$ irb>> require ‘open3‘=> true>> stdin, stdout, stderr = Open3.popen3(‘dc‘)=> [#<IO:fd 6>, #<IO:fd 7>, #<IO:fd 9>, #<Thread:0x816d46c sleep>]>> stdin.puts(5)=> nil>> stdin.puts(10)=> nil>> stdin.puts("+")=> nil>> stdin.puts("p")=> nil>> stdout.gets=> "15\n"
使用該命令我們不僅可以讀取命令的輸出,而且也能寫到命令的標準輸入。這允 許我們靈活地處理與命令的互動。
如果我們需要,popen3
也將給我們標準錯誤。
# (irb continued...)>> stdin.puts("asdfasdfasdfasdf")=> nil>> stderr.gets=> "dc: stack empty\n"
使用popen3 的缺點是 $?
不返回適當的退出狀態。
$ irb>> require ‘open3‘=> true>> stdin, stdout, stderr = Open3.popen3(‘false‘)=> [#<IO:fd 8>, #<IO:fd 10>, #<IO:fd 12>, #<Thread:0x8297644 sleep>]>> $?=> nil>> $?.to_i=> 0
0?false 是假定返回非 0 退出狀態。該缺點帶我們到 Open4。
Open4#popen4
Open4#popen4
是 Ara Howard 建立的 Ruby Gem。它的操作與 open3
相似,例外是我們能從程式獲得退出狀態。popen4
為 subshell 返回進程 ID,這樣我們能從等候的進程獲得退出狀態。(你需要安裝 open4 gem)
$ irb>> require ‘open4‘=> true>> pid, stdin, stdout, stderr = Open4::popen4 "false"=> [17913, #<IO:fd 6>, #<IO:fd 7>, #<IO:fd 9>]>> $?=> nil>> pid=> 17913>> ignored, status = Process::waitpid2 pid=> [17913, #<Process::Status: pid 17913 exit 1>]>> status.to_i=> 256
你也可以作為塊來調用 popen4
,它將自動等候退出狀態。
$ irb>> require ‘open4‘=> true>> status = Open4::popen4("false") do |pid, stdin, stdout, stderr|?> puts "PID #{pid}">> endPID 18535=> #<Process::Status: pid 18535 exit 1>>> puts statuspid 18535 exit 1=> nil