2012-08-30 17 views
9

Digamos que tengo una función como la siguiente, ¿cómo capturo la salida de la llamada Process.spawn? También debería poder matar el proceso si lleva más tiempo que un tiempo de espera especificado.Proceso de horquilla niño con tiempo de espera y salida de captura

Tenga en cuenta que la función también debe ser multiplataforma (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 

Thanks.

Respuesta

12

Puede usar IO.pipe y indicar Process.spawn para utilizar la salida redirigida sin la necesidad de una gema externa.

Por supuesto, sólo a partir de Rubí 1.9.2 (y yo personalmente recomiendo 1.9.3)

La siguiente es una aplicación simple utilizado por Spinach BDD internamente para captar tanto errar y salidas:

# 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 fuente original se encuentra en features/support/filesystem.rb

es muy recomendable que lea la documentación propia de Ruby Process.spawn.

Espero que esto ayude.

PD: Dejé la implementación del tiempo de espera como tarea para usted ;-)

+0

¡Perfecto! Exactamente lo que estaba buscando y mucho más elegante que mi solución :) – thegreendroid

+0

¿Qué diablos significa '_,' en ese código? –

+3

@TamerShlash lee la documentación 'Process.wait2', devuelve una tupla (dos valores), y asignamos uno a' status' y la otra (la primera) se asigna a _, lo cual es una práctica común cuando quieres descartar un valor –

3

Seguí el consejo de Anselm en su publicación en el foro de Ruby here.

La función es similar a esto -

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 

Esto hace el trabajo, pero yo prefiero usar una joya de terceros que envuelve esta funcionalidad. Alguien tiene mejores formas de hacer esto? He intentado Terminator, hace exactamente lo que quiero, pero parece que no funciona en Windows.

Cuestiones relacionadas