2012-08-30 46 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。