Для расширения немного на том, что говорит Brad: много транспортных механизмов для электронной почты и Usenet и других способов переместить данные не "8 битов, чистых", что означает, что символы вне стандартного набора символов ASCII могли бы быть искажены в пути - например, 0x0D мог бы рассматриваться как возврат каретки и превращаться возврат каретки и перевод строки. Основывайте 64 карты все двоичные символы в несколько стандартных букв ASCII и чисел и пунктуации, таким образом, они не будут искажены этот путь.
Мне удалось решить эту мою проблему. Вот подробности с некоторыми пояснениями на тот случай, если кто-то, у кого возникнет аналогичная проблема, найдет эту страницу. Но если вас не интересуют подробности, вот краткий ответ :
Используйте 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
И вот длинный ответ со слишком большим количеством деталей:
Реальная проблема, похоже, в том, что если процесс явно не очищает свой стандартный вывод, то все, что записано в стандартный вывод, буферизуется, а не отправляется, пока процесс не будет завершен , чтобы минимизировать ввод-вывод (это , по-видимому, деталь реализации многих библиотек C, сделанная так, что пропускная способность максимизируется за счет менее частого ввода-вывода). Если вы можете легко изменить процесс, чтобы он регулярно сбрасывал 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 буферизуется, даже если вы запускаете его в случайном порядке. 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
используйте IO.popen
. Этот является хорошим примером.
Ваш код будет выглядеть примерно так:
blender = nil
t = Thread.new do
IO.popen("blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1") do |blender|
blender.each do |line|
puts line
end
end
end
Блендер, вероятно, не печатает разрывы строк, пока это завершение программы. Вместо этого он печатает символ возврата каретки (\ r). Самым простым решением, вероятно, является поиск волшебной опции, которая печатает разрывы строк с индикатором выполнения.
Проблема в том, что IO # получает
(и различные другие методы IO) используют разрыв строки в качестве разделителя . Они будут читать поток, пока не дойдут до символа "\ n" (который не отправляет блендер).
Попробуйте установить разделитель ввода $ / = "\ r"
или используйте блендер .gets ("\ r")
вместо этого.
Кстати, для подобных проблем