为什么在重定向时,来自Ruby的STDERR输出先于早期的STDOUT输出?

时间:2012-01-17 20:47:05

标签: ruby bash

在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相同)并获得输出和异常,但顺序与查看终端输出时的顺序相同。这可以实现吗?

4 个答案:

答案 0 :(得分:9)

这是因为行缓冲与块缓冲。您可以控制缓冲的类型,您可以在希望其输出同步的位置刷新它们,或者您可以等到退出,此时所有内容都会被刷新。除非你以某种方式强制它,否则缓冲取决于输出是否为tty-type 1 文件描述符,因此重定向到管道会改变模式。

具体做法是:

                 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)。

答案 1 :(得分:2)

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

puts "one"
$>.flush
另一方面,STDERR总是立即输出。

答案 2 :(得分:0)

根据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

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

谢谢!

答案 3 :(得分:0)

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

应该这样做