Мне удалось решить эту мою проблему. Вот подробности с некоторыми пояснениями на тот случай, если кто-то, у кого возникнет аналогичная проблема, найдет эту страницу. Но если вас не интересуют подробности, вот краткий ответ:
Используйте 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
# 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
И вот длинный ответ, слишком подробный:
Реальная проблема, похоже, заключается в том, что если процесс явно не сбрасывает свой стандартный вывод, то все, что записано в стандартный вывод, буферизуется, а не отправляется, пока процесс не будет завершен, чтобы минимизировать ввод-вывод (это , по-видимому, em> деталь реализации многих библиотек C, сделанная так, что пропускная способность максимизируется за счет менее частого ввода-вывода). Если вы можете легко изменить процесс, чтобы он регулярно сбрасывал стандартный вывод, то это было бы вашим решением. В моем случае это был блендер, поэтому для такого нуба, как я, было бы немного устрашать изменение исходного кода.
Но когда вы запускаете эти процессы из оболочки, они отображают стандартный вывод в оболочку в реальном времени, и кажется, что стандартный вывод не буферизуется. Я считаю, что он буферизуется только при вызове из другого процесса, но если обрабатывается оболочка, стандартный вывод отображается в реальном времени без буферизации.
Такое поведение можно наблюдать даже с процессом 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
person
ehsanul
schedule
22.07.2009