我正在编写一个脚本,它使用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作为新行,所以这不是问题。
答案 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);
}
}