为什么当我不期望时,Ruby的STDOUT会进行缓冲?

5
以下代码是我目前情况的简化版本。我有一个JSON日志源,我不断地获取并使用puts写入标准输出流。
#!/usr/bin/env ruby

require "json"

loop do
  puts({ value: "foobar" }.to_json)
  sleep 1
end

我希望能够以Unix管道的方式将此脚本的输出传输到 jq 进行进一步处理。在保持“流”友好性的同时,如下运行以上代码:

./my_script | jq

结果为空。然而,如果我在sleep调用后加上一个exit语句,输出就按预期通过管道发送到jq。我能够通过在puts调用之后调用$stdout.flush来解决这个问题。虽然现在它能正常工作了,但我不确定为什么。$stdout.sync默认设置为true(参见IO#sync)。在我的看法中,如果启用了同步,Ruby应该不会进行任何输出缓冲,因此不应需要调用$stdout.flush——但是实际情况却并非如此。

我的后续问题是关于使用tail而非jq。在我看来,我应该能够以同样的方式将文本流管道传入tail,但无论是否调用$stdout.flush,两种方法都不起作用——输出为空。


4
tail 命令可以显示输入内容的最后N行。如果输入内容还没有结束,tail 就无法决定要显示什么。 - mrzasa
3
"$stdout.sync默认为true(请参见IO#sync)" - 这个文档在哪里提到了? - Stefan
3
顺便说一下,对于我来说明确设置STDOUT.sync = true就可以正常工作。默认情况下,如果输出设备是TTY,则自动刷新输出,否则进行缓冲。您可以检查STDOUT.tty? - 当直接写入终端时为 true,当被管道/重定向时为 false - Stefan
4
你也许已经猜到了,在IRB中,$stdout.sync似乎默认为true,但是 ruby -e 'puts $stdout.sync' 可以显示适用于你的脚本的设置。 - Ry-
1
@Ry- 结果并不是这样,yes | tail -f 会挂起,尽管 man 使用“描述符”作为输入名称。 - Aleksei Matiushkin
显示剩余4条评论
1个回答

4

正如 @Ry 在 评论中所指出的,IRB 中默认情况下 $stdout.synctrue,但对于脚本并非总是如此。

因此,您应该设置 $stdout.sync = true 来确保防止缓冲。


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