如何从Ruby程序内部调用shell命令?然后如何将这些命令的输出返回到Ruby中?


当前回答

使用这里的答案和Mihai的答案链接,我组合了一个满足这些要求的函数:

整洁地捕获STDOUT和STDERR,因此当我的脚本从控制台运行时,它们不会“泄漏”。 允许参数作为数组传递给shell,因此不需要担心转义。 捕获命令的退出状态,以便在发生错误时显示出来。

作为奖励,当shell命令成功退出(0)并将任何内容放在STDOUT上时,该命令还将返回STDOUT。在这种情况下,它与system不同,后者在这种情况下只返回true。

代码如下。具体的函数是system_quiet:

require 'open3'

class ShellError < StandardError; end

#actual function:
def system_quietly(*cmd)
  exit_status=nil
  err=nil
  out=nil
  Open3.popen3(*cmd) do |stdin, stdout, stderr, wait_thread|
    err = stderr.gets(nil)
    out = stdout.gets(nil)
    [stdin, stdout, stderr].each{|stream| stream.send('close')}
    exit_status = wait_thread.value
  end
  if exit_status.to_i > 0
    err = err.chomp if err
    raise ShellError, err
  elsif out
    return out.chomp
  else
    return true
  end
end

#calling it:
begin
  puts system_quietly('which', 'ruby')
rescue ShellError
  abort "Looks like you don't have the `ruby` command. Odd."
end

#output: => "/Users/me/.rvm/rubies/ruby-1.9.2-p136/bin/ruby"

其他回答

我最喜欢的是Open3

  require "open3"

  Open3.popen3('nroff -man') { |stdin, stdout, stderr| ... }

您可以使用如下格式方法打印一些信息:

puts format('%s', `ps`)
puts format('%d MB', (`ps -o rss= -p #{Process.pid}`.to_i / 1024))

在这些机制之间进行选择时需要考虑的一些事情是:

你只是想要stdout还是 也需要stderr ?甚至 分离出来吗? 你的产出有多大?你想要 将整个结果保存在内存中? 你想读一些你的吗 在子进程静止时输出 跑步吗? 你需要结果代码吗? 你需要一个Ruby对象吗 表示流程并允许您 按需杀死它?

您可能需要简单的反撇号(' ')、system()和IO。popen到成熟的Kernel.fork/Kernel。执行IO。pipe和IO.select。

如果子进程执行时间过长,您可能还想在混合过程中加入超时。

不幸的是,这在很大程度上取决于情况。

下面是一个基于“何时在Ruby中使用启动子进程的每种方法”的流程图。另请参见“欺骗应用程序,使其误以为标准输出是终端,而不是管道”。

下面是我在OS X上的ruby脚本中使用的一个很酷的脚本(这样我就可以在切换离开窗口后启动脚本并获得更新):

cmd = %Q|osascript -e 'display notification "Server was reset" with title "Posted Update"'|
system ( cmd )