我們時常會與作業系統互動或在 Ruby 中執行 Shell 命令。Ruby為我們提供了完成該任務的諸多方法。
-
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 " >> $? => #<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 "
使用該命令我們不僅可以讀取命令的輸出,而且也能寫到命令的標準輸入。這允 許我們靈活地處理與命令的互動。
如果我們需要,
popen3
也將給我們標準錯誤。# (irb continued...) >> stdin.puts("asdfasdfasdfasdf") => nil >> stderr.gets => "dc: stack empty "
使用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}" >> end PID 18535 => #<Process::Status: pid 18535 exit 1> >> puts status pid 18535 exit 1 => nil
原文來自 tech.natemurray.com
翻譯 dailyrb
許可 CC-by-sa