2011-08-19 12 views
7

Estoy trabajando en un script que usa IO.popen para abrir otro programa y leer continuamente los datos. Es como esto:Ruby IO.popen STDOUT buffering

process = IO.popen(["/the/program", "argument", "argument"]) 

loop do 
    line = process.gets 
    puts "#{line}" 
end 

(en realidad el programa hace algo más que la impresión de la salida, obviamente, - eso es sólo un ejemplo.)

El problema que estoy corriendo en es que popen parece estar amortiguando STDOUT del proceso abierto. Lo he confirmado ejecutando el programa directamente desde un shell y pasando por popen, uno al lado del otro, y el Ruby nunca recibe una línea a la vez. Siempre obtiene múltiples líneas a la vez, y se retrasa.

He intentado

STDOUT.sync = true 

... antes de popen, pero que no ha cambiado nada.

El programa en cuestión definitivamente está usando \ n como una nueva línea, por lo que ese no es el problema.

+0

¿Ha intentado 'process.sync = true'? – DNNX

+0

@ Eddie, ¿te diste cuenta de esto? –

Respuesta

5

¿Tiene la fuente para el otro programa? O bien debe forzar al otro programa para que purgue su salida, o hacer que su script se vea como pty (consulte la lib estándar pty).

Consulte this question para obtener una buena explicación de lo que está sucediendo.

EDITAR: Pty código de ejemplo:

require 'pty' 
PTY.spawn "some-command" do |r,w,p| 
    loop { puts r.gets } 
end 
+0

Esto no imprime la última línea si no termina en una nueva línea (por ejemplo, si some-command es echo -n ABC). –

3

Sospecho /the/program se almacena en búfer cuando se detecta la salida estándar no es un terminal - se puede probar mediante la canalización a través de gato, por ejemplo:

"/the/program" "argument" "argument" | cat 

La respuesta anterior la resolverá si ese es el problema, es decir:

#!/usr/bin/env ruby 

require 'pty' 
PTY.spawn "./the-program testing one Two three" do |r,w,p| 
    loop { puts "GOT: #{r.gets}" } 
end 

Algunos idiomas (por ejemplo, c) detectar si la salida estándar es un terminal y cambiar a la línea amortiguadas - ver Is stdout line buffered, unbuffered or indeterminate by default?

A modo de ejemplo, cuando funciona, he usado un simple script bash para salida de cada argumento y el tiempo, de uno en uno tiempo, con 3 segundos entre medio y el script de ruby ​​funcionó sin problemas. Agregué eof detección para este ejemplo.

escritura de modificación:

contenidos
#!/usr/bin/env ruby 

process = IO.popen(["./the-program", "testing", "one", "Two", "three"]) 

while !process.eof? 
    line = process.gets 
    puts "GOT: #{line}" 
end 

el programa:

#!/bin/bash 

for arg 
do 
    echo $arg 
    date 
    sleep 3 
done 

Probé con la versión 1.9.3 de rubí y 2.1.2

$ ruby ,p 
GOT: testing 
GOT: Mon Jun 16 06:19:00 EST 2014 
GOT: one 
GOT: Mon Jun 16 06:19:03 EST 2014 
GOT: Two 
GOT: Mon Jun 16 06:19:06 EST 2014 
GOT: three 
GOT: Mon Jun 16 06:19:09 EST 2014 
$ 

Si uso un C programa en su lugar, entonces el problema vuelve a ocurrir:

#include <stdio.h> 

main(int argc, char **argv) 
{ 
     int i; 

     for (i=0; i<argc; i++) { 
       printf("%s\n", argv[i]); 
       sleep(3); 
     } 
}