Odniosłem pewien sukces w rozwiązaniu tego mojego problemu. Oto szczegóły wraz z wyjaśnieniami, na wypadek gdyby ktoś miał podobny problem, znalazł tę stronę. Ale jeśli nie dbasz o szczegóły, oto krótka odpowiedź :
Używaj PTY.spawn w następujący sposób (oczywiście z własnym poleceniem):
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
A oto długa odpowiedź , ze zbyt wieloma szczegółami:
Prawdziwym problemem wydaje się być to, że jeśli proces nie opróżnia swojego standardowego wyjścia, to wszystko, co jest zapisywane na standardowe wyjście, jest buforowane, a nie wysyłane, dopóki proces nie zostanie zakończony, aby zminimalizować IO (jest to najwyraźniej szczegół implementacji wielu Biblioteki C, stworzone w taki sposób, że przepustowość jest maksymalizowana poprzez rzadsze IO). Jeśli możesz łatwo zmodyfikować proces tak, aby regularnie opróżniał standardowe wyjście, to byłoby to rozwiązanie. W moim przypadku był to blender, więc trochę onieśmielający dla kompletnego nooba, takiego jak ja, do modyfikowania źródła.
Ale kiedy uruchamiasz te procesy z powłoki, wyświetlają one standardowe wyjście do powłoki w czasie rzeczywistym, a standardowe wyjście nie wydaje się być buforowane. Są one buforowane tylko wtedy, gdy są wywoływane z innego procesu, jak sądzę, ale jeśli zajmujemy się powłoką, standardowe wyjście jest widoczne w czasie rzeczywistym, niebuforowane.
To zachowanie można nawet zaobserwować w przypadku procesu rubinowego jako procesu potomnego, którego dane wyjściowe muszą być gromadzone w czasie rzeczywistym. Po prostu utwórz skrypt, random.rb, z następującym wierszem:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Następnie skrypt ruby, aby go wywołać i zwrócić wynik:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Zobaczysz, że nie otrzymasz wyniku w czasie rzeczywistym, jak można by się spodziewać, ale wszystko naraz. STDOUT jest buforowany, nawet jeśli sam uruchomisz random.rb, nie jest buforowany. Można to rozwiązać, dodając STDOUT.flush
instrukcję wewnątrz bloku w random.rb. Ale jeśli nie możesz zmienić źródła, musisz to obejść. Nie można go przepłukać spoza procesu.
Jeśli podproces może drukować do powłoki w czasie rzeczywistym, musi istnieć sposób, aby przechwycić to również w Rubim w czasie rzeczywistym. I jest. Musisz użyć modułu PTY, zawartego w rdzeniu rubinowym, jak sądzę (w każdym razie 1.8.6). Smutne jest to, że nie jest to udokumentowane. Na szczęście znalazłem kilka przykładów użycia.
Po pierwsze, aby wyjaśnić, czym jest PTY, oznacza to pseudoterminal . Zasadniczo pozwala skryptowi ruby zaprezentować się podprocesowi tak, jakby był prawdziwym użytkownikiem, który właśnie wpisał polecenie w powłoce. Zatem każde zmienione zachowanie, które występuje tylko wtedy, gdy użytkownik uruchomił proces za pośrednictwem powłoki (na przykład STDOUT nie jest buforowane, w tym przypadku) wystąpi. Ukrywanie faktu, że inny proces rozpoczął ten proces, umożliwia gromadzenie STDOUT w czasie rzeczywistym, ponieważ nie jest on buforowany.
Aby to zadziałało ze skryptem random.rb jako dzieckiem, wypróbuj następujący kod:
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