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

Hen*_*k N 6 ruby bash

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

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

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

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

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

Dig*_*oss 9

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

特别:

                 true          false 
              ------------- --------------
$stdout.tty?  line-buffered block-buffered
$stderr.tty?  line-buffered line-buffered
Run Code Online (Sandbox Code Playgroud)

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

$stdout.sync = $stderr.sync = true # or false, of course
Run Code Online (Sandbox Code Playgroud)

我的测试用例:

$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
Run Code Online (Sandbox Code Playgroud)


1.见ttyname(3).


rob*_*rit 2

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

puts "one"
$>.flush
Run Code Online (Sandbox Code Playgroud)

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