Ho avuto un certo successo nel risolvere questo mio problema. Ecco i dettagli, con alcune spiegazioni, nel caso in cui qualcuno che ha un problema simile trovi questa pagina. Ma se non ti interessano i dettagli, ecco la risposta breve :
Usa PTY.spawn nel modo seguente (con il tuo comando ovviamente):
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
Ed ecco la risposta lunga , con troppi dettagli:
Il vero problema sembra essere che se un processo non scarica esplicitamente il suo stdout, allora qualsiasi cosa scritta su stdout viene bufferizzata piuttosto che effettivamente inviata, fino a quando il processo non è terminato, in modo da ridurre al minimo l'IO (questo è apparentemente un dettaglio di implementazione di molti Librerie C, realizzate in modo che il throughput sia massimizzato attraverso IO meno frequenti). Se puoi facilmente modificare il processo in modo che scarichi regolarmente lo stdout, allora questa sarebbe la tua soluzione. Nel mio caso, era un frullatore, quindi un po 'intimidatorio per un noob completo come me modificare la fonte.
Ma quando esegui questi processi dalla shell, visualizzano lo stdout nella shell in tempo reale e lo stdout non sembra essere bufferizzato. È bufferizzato solo quando viene chiamato da un altro processo, credo, ma se viene gestita una shell, lo stdout viene visto in tempo reale, senza buffer.
Questo comportamento può anche essere osservato con un processo Ruby come processo figlio il cui output deve essere raccolto in tempo reale. Basta creare uno script, random.rb, con la seguente riga:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Quindi uno script ruby per chiamarlo e restituirne l'output:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Vedrai che non ottieni il risultato in tempo reale come potresti aspettarti, ma tutto in una volta dopo. STDOUT viene bufferizzato, anche se esegui random.rb da solo, non è bufferizzato. Questo può essere risolto aggiungendo STDOUT.flush
un'istruzione all'interno del blocco in random.rb. Ma se non puoi cambiare la fonte, devi aggirare questo problema. Non puoi lavarlo dall'esterno del processo.
Se il sottoprocesso può stampare sulla shell in tempo reale, allora ci deve essere un modo per catturarlo anche con Ruby in tempo reale. E c'è. Devi usare il modulo PTY, incluso in ruby core credo (1.8.6 comunque). La cosa triste è che non è documentata. Ma ho trovato fortunatamente alcuni esempi di utilizzo.
Innanzitutto, per spiegare cos'è PTY, sta per pseudo terminale . Fondamentalmente, consente allo script ruby di presentarsi al sottoprocesso come se fosse un utente reale che ha appena digitato il comando in una shell. Quindi si verificherà qualsiasi comportamento alterato che si verifica solo quando un utente ha avviato il processo tramite una shell (come lo STDOUT non bufferizzato, in questo caso). Nascondere il fatto che un altro processo ha avviato questo processo consente di raccogliere lo STDOUT in tempo reale, poiché non viene bufferizzato.
Per fare in modo che funzioni con lo script random.rb come figlio, prova il codice seguente:
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