【问题标题】:Why does STDERR output from Ruby come before earlier STDOUT output when redirecting?为什么重定向时来自 Ruby 的 STDERR 输出在较早的 STDOUT 输出之前?
【发布时间】:2012-01-17 20:47:05
【问题描述】:

在 bash 中,这会以预期的顺序给出输出:

ruby -e "puts 'one'; raise 'two'"
one
-e:1:in `<main>': two (RuntimeError)

但是如果我将 STDERR 重定向到 STDOUT,我会在输出之前收到错误,这是我不想要的:

ruby -e "puts 'one'; raise 'two'" 2>&1 | cat
-e:1:in `<main>': two (RuntimeError)
one

我想将输出重定向到一个文本文件(它的行为方式与上面的cat 相同)并获取输出和异常,但与在终端中查看输出时的顺序相同。这可以实现吗?

【问题讨论】:

  • STDEER 总是打印到流中,而 STDOUT 通常被缓冲打印。

标签: ruby bash


【解决方案1】:

发生这种情况是因为行缓冲与块缓冲。你可以控制缓冲的类型,你可以在你希望它们的输出同步的时候刷新它们,或者你可以等到退出,此时所有的东西都被刷新。除非您以一种或另一种方式强制它,否则缓冲取决于输出是否是 tty-type1 文件描述符,因此重定向到管道会改变模式。

具体来说:

                 true          false 
              ------------- --------------
$stdout.tty?  line-buffered block-buffered
$stderr.tty?  line-buffered line-buffered

您可以使用相同的方式配置它们:

$stdout.sync = $stderr.sync = true # or false, of course

我的测试用例:

$stdout.sync = $stderr.sync = true
$stdout.puts 'stdout a'
sleep 2
$stdout.puts 'stdout b'
sleep 2
$stderr.puts 'stderr a'
sleep 2
$stderr.puts 'stderr b'
sleep 2


1。参见 ttyname(3)。

【讨论】:

  • 仍有帮助的答案。
【解决方案2】:

这是因为 STDOUT 并不总是立即输出,要强制它输出你使用IO#flush:

puts "one"
$>.flush

另一方面,STDERR 总是立即输出。

【讨论】:

  • 如果需要多次刷新缓冲区,IO#flush 是更好的选择。 flush 是单冲,sync 离开手柄向下,你必须摇动它才能让它停止运行。 :-)
【解决方案3】:

根据 Maurício 和 Gir Loves Tacos 的回答,我想出了这个(通过 How to turn on STDOUT.sync in ruby from the command line):

ruby -r "/tmp/sync.rb" -e "puts 'one'; raise 'two'" 2>&1 | cat
one
-e:1:in `<main>': two (RuntimeError)

/tmp/sync.rb 包含在哪里

STDOUT.sync=true

或者,如果您可以修改脚本本身,请将该行添加到开头。

谢谢!

【讨论】:

【解决方案4】:

ruby -e STDOUT.sync=true -e "puts 'one'; raise 'two'" 2&gt;&amp;1 | cat

应该这样做

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多