在 Ruby 中執行 Shell 命令的 6 種方法

咱們時常會與操做系統交互或在 Ruby 中執行 Shell 命令。Ruby爲咱們提供了完成該任務的諸多方法。html

  1. Execshell

    Kernel#exec 經過執行給定的命令來替換當前進程,例如:ruby

    $ irb
    >> exec 'echo "hello $HOSTNAME"'
    hello codefun
    $

    注意 exec 利用 echo 命令替換了 irb 進程,而後退出。由於 Ruby 實際上結束了該方法,因此只能有限使用。該方法的缺點是,你沒法從 Ruby 腳本中知道命令是執行成功仍是失敗。ui

  2. 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)命令。code

    新手注意:Unix 命令執行成功退出碼爲 0,不然爲非 0。htm

    若是咱們想知道「命令是否執行成功呢?」,使用 system 就很好。然而,咱們時常想要捕獲命令的輸出,並在程序中使用。對象

  3. 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 捕獲。

  4. 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

  5. Open3#popen3

    Ruby 標準庫包含 Open3 類。它易用,並能返回標準輸入、標準輸出、以及標準 錯誤。在本例中,讓咱們使用交互命令 dc。dc 是從標準輸入讀取的逆波計算器(reverse-polish calculator)。咱們先 push 兩個數字和一個操做符到堆棧 中。而後咱們使用 p 來打印輸出結果。下面咱們 push 五、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。

  6. 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

相關文章
相關標籤/搜索