在 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

相关文章
相关标签/搜索