Saya telah berhasil memecahkan masalah saya ini. Berikut adalah detailnya, dengan beberapa penjelasan, jika ada orang yang memiliki masalah serupa menemukan halaman ini. Tetapi jika Anda tidak peduli dengan detailnya, inilah jawaban singkatnya :
Gunakan PTY.spawn dengan cara berikut (dengan perintah Anda sendiri tentunya):
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
Dan inilah jawaban panjangnya , dengan terlalu banyak detail:
Masalah sebenarnya tampaknya adalah bahwa jika suatu proses tidak secara eksplisit menghapus stdout-nya, maka apa pun yang ditulis ke stdout di-buffer daripada benar-benar dikirim, sampai proses selesai, untuk meminimalkan IO (ini tampaknya merupakan detail implementasi dari banyak Library C, dibuat agar throughput dimaksimalkan melalui IO yang lebih jarang). Jika Anda dapat dengan mudah memodifikasi prosesnya sehingga stdout secara teratur, maka itu akan menjadi solusi Anda. Dalam kasus saya, ini adalah blender, jadi agak mengintimidasi untuk noob lengkap seperti saya untuk memodifikasi sumbernya.
Tetapi ketika Anda menjalankan proses ini dari shell, mereka menampilkan stdout ke shell secara real-time, dan stdout tampaknya tidak di-buffer. Ini hanya buffer ketika dipanggil dari proses lain yang saya percaya, tetapi jika shell sedang ditangani, stdout terlihat secara real time, tanpa buffer.
Perilaku ini bahkan dapat diamati dengan proses ruby sebagai proses anak yang keluarannya harus dikumpulkan secara real time. Buat saja skrip, random.rb, dengan baris berikut:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Kemudian skrip ruby untuk memanggilnya dan mengembalikan outputnya:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Anda akan melihat bahwa Anda tidak mendapatkan hasil secara real-time seperti yang Anda harapkan, tetapi sekaligus setelahnya. STDOUT sedang di-buffer, meskipun jika Anda menjalankan random.rb sendiri, itu tidak di-buffer. Ini dapat diselesaikan dengan menambahkan STDOUT.flush
pernyataan di dalam blok secara random.rb. Tetapi jika Anda tidak dapat mengubah sumbernya, Anda harus mengatasinya. Anda tidak dapat membersihkannya dari luar proses.
Jika subproses dapat mencetak ke shell secara real-time, maka harus ada cara untuk menangkap ini dengan Ruby secara real-time juga. Dan ada. Anda harus menggunakan modul PTY, termasuk dalam ruby core yang saya percaya (1.8.6 lagian). Yang menyedihkan adalah itu tidak didokumentasikan. Tapi untungnya saya menemukan beberapa contoh penggunaan.
Pertama, untuk menjelaskan apa itu PTY adalah singkatan dari pseudo terminal . Pada dasarnya, ini memungkinkan skrip ruby untuk menampilkan dirinya sendiri ke subproses seolah-olah itu adalah pengguna sungguhan yang baru saja mengetik perintah ke dalam shell. Jadi setiap perubahan perilaku yang terjadi hanya ketika pengguna telah memulai proses melalui shell (seperti STDOUT tidak sedang buffer, dalam kasus ini) akan terjadi. Menyembunyikan fakta bahwa proses lain telah memulai proses ini memungkinkan Anda mengumpulkan STDOUT secara real-time, karena tidak sedang buffer.
Untuk membuat ini bekerja dengan skrip random.rb sebagai anak, coba kode berikut:
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