摆脱Ruby的stdin/stdout缓冲

7

我想编写一个 Ruby 脚本来过滤一个正在被追踪的文件的输出 (tail -f log.log | ./my_filter.rb)。我相信我已经同步读取了标准输入和标准输出,但我仍然看到我的输出是延迟批量输出的,每次大约 20 行,而不是实时输出。

我可以通过以下简单的代码复现问题:

#!/usr/bin/ruby     
                
$stdout.sync = true 
$stdin.sync = true  
                
ARGF.each do |line| 
  puts line         
end                 

我是否缺少一些设置来消除缓冲区,或者类似的东西?

编辑: 澄清一下,如果我只是使用tail -f命令查看日志,那么每秒钟会有很多行被写入。


2
如果你只是运行 tail -f log.log,那么它会实时输出吗? - Petr Skocik
1
日志文件是如何创建的?我怀疑无论什么进程正在创建它,都会缓冲其输出,因此您的 Ruby 脚本只能看到它们被写入的块。 - matt
正是操作系统本身在数据通过管道时缓冲您的数据。 - Rein
当我只是使用 tail -f 命令查看日志时,更新流是实时的,每秒钟有很多行,所以我认为问题出在我的脚本上。如果我将日志输入到 grep 或其他命令行工具中,一切也都是实时的。 - Michael
两个非 Ruby 的解决方案:http://unix.stackexchange.com/a/297672/14907 - akostadinov
3个回答

4
如果你正在处理文件,你可能需要使用IO#fsync,它的作用是:
立即将ios中所有缓冲数据写入磁盘。请注意,fsync与使用IO#sync=不同。后者确保数据从Ruby的缓冲区中刷新,但不能保证底层操作系统实际将其写入磁盘。
如果你只是处理标准输入和输出,也可以尝试要求io/console,看看是否使用IO::console#ioflush能够给你所需的行为。文档说:
在内核中刷新输入和输出缓冲区。 你必须要求‘io/console’才能使用此方法。
例如,考虑以下内容:
require 'io/console'

ARGF.each do |line|
  $stdout.puts line
  $stdout.ioflush
end

2
这并没有真正回答问题。问题是如何使执行程序的随机输出非缓冲或最多行缓冲。在每个puts之后添加调用相当不可行和不可取。 - akostadinov
1
@akostadinov 老旧了,但是 $stdout.sync = true 应该可以解决问题。 - Frizlab

3
这是一个老问题,但是我认为被接受的答案并没有完全回答这个问题。
你可以在程序开头加上$stdout.sync = true来使输出同步到标准输出(如果需要,可以使用require 'io/console')。

1

这个主题的标题包括标准输入和标准输出,因此除了其他回答之外,没有一个回答涉及标准输入:

$stdin.iflush # Discard anything currently in $stdin's byte buffer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接