He tenido cierto éxito resolviendo este problema mío. Aquí están los detalles, con algunas explicaciones, en caso de que alguien que tenga un problema similar encuentre esta página. Pero si no le interesan los detalles, aquí está la respuesta corta :
Utilice PTY.spawn de la siguiente manera (con su propio comando, por supuesto):
require 'pty'
cmd = "blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1"
begin
PTY.spawn( cmd ) do |stdout, stdin, pid|
begin
stdout.each { |line| print line }
rescue Errno::EIO
puts "Errno:EIO error, but this probably just means " +
"that the process has finished giving output"
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end
Y aquí está la respuesta larga , con demasiados detalles:
El problema real parece ser que si un proceso no vacía explícitamente su stdout, entonces todo lo que se escribe en stdout se almacena en búfer en lugar de enviarse realmente, hasta que se realiza el proceso, para minimizar IO (esto aparentemente es un detalle de implementación de muchos Bibliotecas C, creadas para maximizar el rendimiento mediante IO menos frecuentes). Si puede modificar fácilmente el proceso para que se descargue con regularidad, entonces esa sería su solución. En mi caso, era blender, por lo que un poco intimidante para un novato como yo, modificar la fuente.
Pero cuando ejecuta estos procesos desde el shell, muestran stdout al shell en tiempo real, y stdout no parece estar almacenado en búfer. Creo que solo se almacena en búfer cuando se llama desde otro proceso, pero si se está tratando con un shell, la salida estándar se ve en tiempo real, sin búfer.
Este comportamiento se puede observar incluso con un proceso ruby como proceso hijo cuya salida debe recopilarse en tiempo real. Simplemente cree un script, random.rb, con la siguiente línea:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Luego, un script ruby para llamarlo y devolver su salida:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Verá que no obtiene el resultado en tiempo real como cabría esperar, sino todo a la vez después. STDOUT se almacena en búfer, aunque si ejecuta random.rb usted mismo, no se almacena en búfer. Esto se puede resolver agregando una STDOUT.flush
declaración dentro del bloque en random.rb. Pero si no puede cambiar la fuente, debe solucionarlo. No puede vaciarlo desde fuera del proceso.
Si el subproceso puede imprimir en shell en tiempo real, entonces debe haber una forma de capturar esto con Ruby también en tiempo real. Y ahí está. Tienes que usar el módulo PTY, incluido en ruby core, creo (1.8.6 de todos modos). Lo triste es que no está documentado. Pero, afortunadamente, encontré algunos ejemplos de uso.
Primero, para explicar qué es PTY, significa pseudo terminal . Básicamente, permite que el script ruby se presente al subproceso como si fuera un usuario real que acaba de escribir el comando en un shell. Por lo tanto, se producirá cualquier comportamiento alterado que ocurra solo cuando un usuario haya iniciado el proceso a través de un shell (como, en este caso, que STDOUT no se almacena en búfer). Ocultar el hecho de que otro proceso ha iniciado este proceso le permite recopilar el STDOUT en tiempo real, ya que no se almacena en búfer.
Para que esto funcione con el script random.rb como hijo, pruebe el siguiente código:
require 'pty'
begin
PTY.spawn( "ruby random.rb" ) do |stdout, stdin, pid|
begin
stdout.each { |line| print line }
rescue Errno::EIO
end
end
rescue PTY::ChildExited
puts "The child process exited!"
end