9

假设我有一个像下面这样的函数,我如何捕获 Process.spawn 调用的输出?如果它花费的时间超过指定的超时时间,我也应该能够终止该进程。

请注意,该功能还必须是跨平台的(Windows/Linux)。

def execute_with_timeout!(command)
  begin
    pid = Process.spawn(command)     # How do I capture output of this process?
    status = Timeout::timeout(5) {
      Process.wait(pid)
    }
  rescue Timeout::Error
    Process.kill('KILL', pid)
  end
end

谢谢。

4

2 回答 2

12

您可以使用IO.pipe并告诉Process.spawn使用重定向的输出,而无需外部 gem。

当然,只能从 Ruby 1.9.2 开始(而且我个人推荐 1.9.3)

以下是Spinach BDD在内部用于捕获 out 和 err 输出的简单实现:

# stdout, stderr pipes
rout, wout = IO.pipe
rerr, werr = IO.pipe

pid = Process.spawn(command, :out => wout, :err => werr)
_, status = Process.wait2(pid)

# close write ends so we could read them
wout.close
werr.close

@stdout = rout.readlines.join("\n")
@stderr = rerr.readlines.join("\n")

# dispose the read ends of the pipes
rout.close
rerr.close

@last_exit_status = status.exitstatus

原始来源在features/support/filesystem.rb

强烈建议您阅读 Ruby 自己的Process.spawn文档。

希望这可以帮助。

PS:我将超时实现作为作业留给你;-)

于 2012-08-30T17:36:27.087 回答
3

我在此处的 Ruby 论坛上遵循了 Anselm 的建议。

该功能如下所示 -

def execute_with_timeout!(command)
  begin
    pipe = IO.popen(command, 'r')
  rescue Exception => e
    raise "Execution of command #{command} unsuccessful"
  end

  output = ""
  begin
    status = Timeout::timeout(timeout) {
      Process.waitpid2(pipe.pid)
      output = pipe.gets(nil)
    }
  rescue Timeout::Error
    Process.kill('KILL', pipe.pid)
  end
  pipe.close
  output
end

这可以完成工作,但我宁愿使用包装此功能的第三方 gem。有人有更好的方法吗?我已经尝试过Terminator,它完全符合我的要求,但它似乎不适用于 Windows。

于 2012-08-30T05:28:51.433 回答