2012-08-30 40 views
9

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

请注意,该功能还必须是跨平台的(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 

谢谢。

回答

12

您可以使用IO.pipe并告诉Process.spawn使用重定向输出而不需要外部宝石。

当然,仅使用Ruby 1.9.2开始(和我个人推荐1.9.3)

以下是用于通过在内部Spinach BDD捕获既出和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:我离开超时实现作为你的功课;-)

+0

完美!正是我所追求的,比我的解决方案更优雅:) – thegreendroid

+0

'_,'在这段代码中意味着什么? –

+3

@TamerShlash读取'Process.wait2'文档,它返回一个元组(两个值),我们将其中一个赋给'status',另一个(第一个)赋给_,这是您想要丢弃时的惯例一个值。 –

3

我在Ruby论坛here上关注了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 

这做工作,但我宁愿使用第三方的宝石,包装此功能。任何人有更好的方法来做到这一点?我试过Terminator,它确实是我想要的,但它似乎不适用于Windows。