Ruby IO.popen STDOUT缓冲

时间:2011-08-19 04:51:39

标签: ruby popen

我正在编写一个脚本,它使用IO.popen打开另一个程序并不断读取数据。就像这样:

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

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

(实际的程序不仅仅是打印输出,显然 - 这只是一个例子。)

我遇到的问题是popen似乎是从打开的进程中缓冲STDOUT。我已经通过直接从shell运行程序并通过popen并排运行来确认这一点,并且Ruby一次不会获得一行。它总是一次获得多行,并且会延迟。

我试过

STDOUT.sync = true

......在popen之前,但这并没有改变任何东西。

有问题的程序肯定是使用\ n作为新行,所以这不是问题。

2 个答案:

答案 0 :(得分:5)

你有其他程序的来源吗?您需要强制其他程序刷新其输出,或使您的脚本看起来像pty(请参阅pty标准库)。

有关正在发生的事情的详细解释,请参阅this question

编辑:pty示例代码:

require 'pty'
PTY.spawn "some-command" do |r,w,p|
  loop { puts r.gets }
end

答案 1 :(得分:3)

我怀疑/the/program在检测到stdout不是终端时正在缓冲 - 您可以通过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是否为终端并更改为缓冲行 - 请参阅Is stdout line buffered, unbuffered or indeterminate by default?

作为一个例子,当它工作时,我使用一个简单的bash脚本输出每个参数和时间,一次一个,介于两者之间,并且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);
        }
}