Мне удалось решить эту мою проблему. Вот подробности с некоторыми пояснениями на тот случай, если кто-то, у кого есть подобная проблема, найдет эту страницу. Но если вас не интересуют подробности, вот краткий ответ :
Используйте PTY.spawn следующим образом (конечно, своей собственной командой):
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
И вот длинный ответ со слишком большим количеством деталей:
Реальная проблема, похоже, заключается в том, что если процесс явно не сбрасывает свой стандартный вывод, то все, что написано в стандартный вывод, буферизуется, а не отправляется, пока процесс не будет завершен, чтобы минимизировать ввод-вывод (это, по- видимому, деталь реализации многих Библиотеки C, созданные таким образом, чтобы максимизировать пропускную способность за счет менее частого ввода-вывода). Если вы можете легко изменить процесс, чтобы он регулярно очищал стандартный вывод, то это было бы вашим решением. В моем случае это был блендер, поэтому для такого нуба, как я, было бы немного устрашать изменение исходного кода.
Но когда вы запускаете эти процессы из оболочки, они отображают stdout в оболочке в реальном времени, и stdout не кажется буферизированным. Я считаю, что он буферизуется только при вызове из другого процесса, но если обрабатывается оболочка, стандартный вывод отображается в реальном времени без буферизации.
Такое поведение можно наблюдать даже с процессом ruby в качестве дочернего процесса, выходные данные которого должны собираться в реальном времени. Просто создайте сценарий random.rb со следующей строкой:
5.times { |i| sleep( 3*rand ); puts "#{i}" }
Затем сценарий ruby для его вызова и возврата его вывода:
IO.popen( "ruby random.rb") do |random|
random.each { |line| puts line }
end
Вы увидите, что вы получите результат не в реальном времени, как вы могли бы ожидать, а сразу после этого. STDOUT буферизуется, даже если вы запускаете random.rb самостоятельно, он не буферизуется. Это можно решить, добавив STDOUT.flush
инструкцию внутри блока в random.rb. Но если вы не можете изменить источник, вам нужно обойти это. Вы не можете смыть его извне.
Если подпроцесс может печатать в оболочке в реальном времени, тогда должен быть способ зафиксировать это с помощью Ruby в реальном времени. Так и есть. Вы должны использовать модуль PTY, который, я полагаю, включен в ядро ruby (в любом случае 1.8.6). Печально то, что это не задокументировано. Но, к счастью, я нашел несколько примеров использования.
Во-первых, чтобы объяснить, что такое PTY, это означает псевдотерминал . По сути, это позволяет сценарию ruby представить себя подпроцессу, как если бы это был реальный пользователь, который только что ввел команду в оболочку. Таким образом, любое измененное поведение, которое возникает только тогда, когда пользователь запустил процесс через оболочку (например, в данном случае STDOUT не буферизуется), произойдет. Сокрытие того факта, что этот процесс был запущен другим процессом, позволяет вам собирать STDOUT в реальном времени, поскольку он не буферизируется.
Чтобы это работало с дочерним скриптом random.rb, попробуйте следующий код:
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