In bash, this gives the output in the expected order:
ruby -e "puts 'one'; raise 'two'"
one
-e:1:in `<main>': two (RuntimeError)
But if I redirect STDERR to STDOUT, I get the error before the output, which I do not want:
ruby -e "puts 'one'; raise 'two'" 2>&1 | cat
-e:1:in `<main>': two (RuntimeError)
one
I want to redirect the output to a text file (it behaves the same way as cat
above) and get both output and exceptions, but in the same order as when looking at the output in my terminal. Can this be achieved?
By default, stderr is typically connected to the same place as stdout, i.e. the current terminal.
In Ruby, $stdout represents the current standard output and a global variable. It means we can also use it to output data.
It's because STDOUT doesn't always output right away, to force it to output you use IO#flush
:
puts "one"
$>.flush
STDERR on the other hand always outputs immediately.
This happens because of line-buffering vs block-buffering. You can control the type of buffering, you can flush them at the point you want their output to be synced, or you can just wait until exit at which point everything gets flushed. Unless you force it one way or the other, buffering depends on whether the output is a tty-type1 file descriptor, so redirection into a pipe changes the mode.
Specifically:
true false
------------- --------------
$stdout.tty? line-buffered block-buffered
$stderr.tty? line-buffered line-buffered
You can configure them both the same way with:
$stdout.sync = $stderr.sync = true # or false, of course
My test case:
$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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With