Tive algum sucesso em resolver este meu problema. Aqui estão os detalhes, com algumas explicações, caso alguém com problema semelhante encontre esta página. Mas se você não se importa com os detalhes, aqui está a resposta curta :
Use PTY.spawn da seguinte maneira (com seu próprio comando, é claro):
require 'pty'
cmd = "blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1"
begin
PTY.spawn( cmd ) do |stdout, stdin, pid|
begin
# Do stuff with the output here. Just printing to show it works
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
E aqui está a resposta longa , com muitos detalhes:
O verdadeiro problema parece ser que se um processo não liberar explicitamente seu stdout, então qualquer coisa gravada em stdout é armazenada em buffer em vez de realmente enviada, até que o processo seja concluído, para minimizar IO (este é aparentemente um detalhe de implementação de muitos Bibliotecas C, feitas de forma que o rendimento seja maximizado por meio de E / S menos frequente). Se você puder modificar facilmente o processo para que ele libere o stdout regularmente, essa seria sua solução. No meu caso, era o liquidificador, então um pouco intimidante para um noob completo como eu modificar a fonte.
Mas quando você executa esses processos a partir do shell, eles exibem stdout no shell em tempo real, e o stdout não parece estar em buffer. Ele só é armazenado em buffer quando chamado de outro processo, eu acredito, mas se um shell está sendo tratado, o stdout é visto em tempo real, sem buffer.
Esse comportamento pode até ser observado com um processo ruby como o processo filho cuja saída deve ser coletada em tempo real. Basta criar um script, random.rb, com a seguinte linha:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Em seguida, um script ruby para chamá-lo e retornar sua saída:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Você verá que não obtém o resultado em tempo real como poderia esperar, mas tudo de uma vez depois. STDOUT está sendo armazenado em buffer, embora se você executar random.rb por conta própria, ele não será armazenado em buffer. Isso pode ser resolvido adicionando uma STDOUT.flush
instrução dentro do bloco em random.rb. Mas se você não pode mudar a fonte, você tem que contornar isso. Você não pode liberá-lo de fora do processo.
Se o subprocesso pode imprimir no shell em tempo real, então deve haver uma maneira de capturar isso com Ruby também em tempo real. E aqui está. Você tem que usar o módulo PTY, incluído no ruby core, eu acredito (1.8.6 de qualquer maneira). O triste é que não está documentado. Mas eu encontrei alguns exemplos de uso felizmente.
Primeiro, para explicar o que é PTY, significa pseudo terminal . Basicamente, ele permite que o script ruby se apresente ao subprocesso como se fosse um usuário real que acabou de digitar o comando em um shell. Portanto, qualquer comportamento alterado que ocorra apenas quando um usuário iniciar o processo por meio de um shell (como o STDOUT não sendo armazenado em buffer, neste caso) ocorrerá. Esconder o fato de que outro processo foi iniciado permite que você colete o STDOUT em tempo real, pois ele não está sendo armazenado em buffer.
Para fazer isso funcionar com o script random.rb como filho, tente o seguinte 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