2011-08-19 21 views
7

Sto lavorando a uno script che utilizza IO.popen per aprire un altro programma e leggere continuamente i dati. E 'in questo modo:Ruby IO.popen buffering STDOUT

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

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

(Il programma vero e proprio non si limita a stampare l'uscita, ovviamente - questo è solo un esempio.)

Il problema che sto funzionando in è che popen sembra essere il buffering STDOUT dal processo aperto. L'ho confermato eseguendo il programma direttamente da una shell e tramite il popen, fianco a fianco, e il Ruby non ottiene mai una riga alla volta. Ottiene sempre più righe alla volta e viene ritardato.

Ho provato

STDOUT.sync = true 

... prima di popen, ma che non è cambiato nulla.

Il programma in questione usa definitivamente \ n come una nuova riga, quindi non è questo il problema.

+0

Hai provato 'process.sync = true'? – DNNX

+0

@ Eddie l'hai capito? –

risposta

5

Avete la fonte per l'altro programma? È necessario forzare l'altro programma a svuotare l'output o rendere lo script simile a un pty (vedere la lib standard di pty).

Vedere this question per una buona spiegazione di cosa sta succedendo.

EDIT: pty codice di esempio:

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

Non riesce a stampare l'ultima riga se non finisce in una nuova riga (ad esempio se qualche comando è echo -n ABC). –

3

Ho il sospetto /the/program è il buffering quando rileva stdout non è un terminale - è possibile testare convogliando attraverso gatto, ad esempio:

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

La risposta sopra lo risolverà se questo è il problema, ovvero:

#!/usr/bin/env ruby 

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

Alcune lingue (ad esempio C) rilevare se stdout è un terminale e cambiare per la linea bufferizzati - vedi Is stdout line buffered, unbuffered or indeterminate by default?

Ad esempio quando funziona, ho usato un semplice script bash per emettere ogni argomento e il tempo, uno alla tempo, con 3 secondi in mezzo e lo script ruby ​​ha funzionato senza problemi. Ho aggiunto il rilevamento di eof per questo esempio.

script modificato:

contenuti
#!/usr/bin/env ruby 

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

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

il-programma:

#!/bin/bash 

for arg 
do 
    echo $arg 
    date 
    sleep 3 
done 

ho provato con rubino versione 1.9.3 e 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 
$ 

Se io uso un C programma invece, quindi il problema si ripresenta:

#include <stdio.h> 

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

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