Ruby IO.popen буферизация STDOUT - PullRequest
       1

Ruby IO.popen буферизация STDOUT

7 голосов
/ 19 августа 2011

Я работаю над сценарием, который использует IO.popen, чтобы открыть другую программу и постоянно читать данные.Это примерно так:

process = IO.popen(["/the/program", "argument", "argument"])

loop do
  line = process.gets
  puts "#{line}"
end

(Реальная программа делает больше, чем просто печатает вывод, очевидно - это всего лишь пример.)

Проблема, с которой я столкнулся, заключается в том, чтобуферизовать STDOUT из открытого процесса.Я подтвердил это, запустив программу непосредственно из командной строки и через popen, бок о бок, и Ruby никогда не получает по одной строке за раз.Он всегда получает несколько строк за раз и задерживается.

Я пробовал

STDOUT.sync = true

... перед открытием, но это ничего не изменило.

Рассматриваемая программа определенно использует \ n в качестве новой строки, поэтому проблема не в этом.

Ответы [ 2 ]

5 голосов
/ 19 августа 2011

Есть ли у вас источник для другой программы? Вам либо нужно заставить другую программу сбрасывать вывод, либо сделать ваш скрипт похожим на pty (см. pty стандартную библиотеку).

См. этот вопрос для хорошего объяснения того, что происходит.

РЕДАКТИРОВАТЬ : pty пример кода:

require 'pty'
PTY.spawn "some-command" do |r,w,p|
  loop { puts r.gets }
end
3 голосов
/ 16 июня 2014

Я подозреваю, что /the/program буферизуется, когда обнаруживает, что стандартный вывод не является терминалом - вы можете проверить это, передав по трубопроводу cat, например:

"/the/program" "argument" "argument" | cat

Ответ выше, решит его, если это проблема, например:

#!/usr/bin/env ruby

require 'pty'
PTY.spawn "./the-program testing one Two three" do |r,w,p|
  loop { puts "GOT: #{r.gets}" }  
end

Некоторые языки (например, C) определяют, является ли stdout терминалом, и переключаются на буферизованную строку - см. Строка stdout буферизована, небуферизована или неопределена по умолчанию?

В качестве примера, когда он работает, я использовал простой скрипт bash для вывода каждого аргумента и времени, по одному, с перерывом в 3 секунды, и скрипт ruby ​​работал без проблем.Для этого примера я добавил функцию обнаружения eof.

Модифицированный скрипт:

#!/usr/bin/env ruby

process = IO.popen(["./the-program", "testing", "one", "Two", "three"])

while !process.eof?
  line = process.gets
  puts "GOT: #{line}"
end

Содержимое программы:

#!/bin/bash

for arg
do
  echo $arg
  date
  sleep 3
done

Я пытался с ruby ​​версии 1.9.3 и 2.1.2

$ ruby ,p
GOT: testing
GOT: Mon Jun 16 06:19:00 EST 2014
GOT: one
GOT: Mon Jun 16 06:19:03 EST 2014
GOT: Two
GOT: Mon Jun 16 06:19:06 EST 2014
GOT: three
GOT: Mon Jun 16 06:19:09 EST 2014
$ 

Если вместо этого я использую программу на C, проблема повторяется:

#include <stdio.h>

main(int argc, char **argv)
{
        int i;

        for (i=0; i<argc; i++) {
                printf("%s\n", argv[i]);
                sleep(3);
        }
}
...