2012-08-30 20 views
9

Dire che ho una funzione come di seguito, come posso acquisire l'output della chiamata Process.spawn? Dovrei anche essere in grado di terminare il processo se richiede più tempo di un timeout specificato.Processo figlio fork con timeout e output di acquisizione

Si noti che la funzione deve anche essere multipiattaforma (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 

Grazie.

risposta

12

È possibile utilizzare IO.pipe e indicare a Process.spawn di utilizzare l'output reindirizzato senza la necessità di gemme esterne.

Naturalmente, solo a partire con Ruby 1.9.2 (e consiglio personalmente 1.9.3)

La seguente è una semplice implementazione utilizzata Spinach BDD internamente per catturare entrambi e err uscite:

# 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 

La fonte originale è in features/support/filesystem.rb

è altamente raccomandato di leggere propria documentazione di Ruby Process.spawn.

Spero che questo aiuti.

PS: ho lasciato l'implementazione del timeout come compito per te ;-)

+0

Perfetto! Esattamente quello che cercavo e molto più elegante della mia soluzione :) – thegreendroid

+0

Cosa diavolo significa "_," nel codice? –

+3

@TamerShlash legge la documentazione 'Process.wait2', restituisce una tupla (due valori), e ne assegniamo una a' status' e l'altra (la prima) è assegnata a _, che è pratica comune quando si vuole scartare un valore. –

3

Ho seguito il consiglio di Anselm nel suo post sul forum Ruby here.

La funzione assomiglia a questo -

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 

Questo fa il lavoro, ma preferisce utilizzare una gemma di terze parti che avvolge questa funzionalità. Qualcuno ha modi migliori per farlo? Ho provato Terminator, fa esattamente quello che voglio ma non sembra funzionare su Windows.

Problemi correlati