I want to know the output stream being used by Ruby to print these things at the command-line:
irb(main):001:0> a="test"
=> "test"
irb(main):002:0> puts a
test
=> nil
irb(main):003:0> a
=> "test"
Is $stdout
used for irb(main):002:0>
and irb(main):003:0>
? And, is there any change in the value of $stdout
between those two invocations?
Also, can some one point me to the Ruby source from where these things get printed/written?
Yes. And it's easy to test/prove to yourself. Try this at the command-line:
ruby -e 'puts "foo"' > test.out
cat test.out
The output will be:
foo
Ruby uses the STDOUT channel to output to the console. The OS then redirects that STDOUT to "test.out".
Try it with:
ruby -e 'STDOUT.puts "foo"' > test.out
and you'll get the same result.
If we do:
ruby -e 'STDERR.puts "foo"' > test.out
foo
cat test.out
You'll see nothing in the file, but "foo" will have been written to the console on the STDERR channel.
Ruby defines $stdout
as a global you can change, and STDOUT
as a constant, which you shouldn't change. Similarly, $stderr
and STDERR
are available.
Now, here's where it gets fun, and proves your question. Try this:
ruby -e '$stdout = STDERR; puts "foo"' > test.out
and you'll have the same results as when I output to STDERR
, because, at puts
was using the value for $stdout
to select the output stream, and wrote to STDERR. Those stream values are picked up by Ruby from the OS when the interpreter starts, and are remembered during the run-time of the script. You can change them if necessary and Ruby will forget those settings when the interpreter exits, and reset itself to its normal state the next time.
You shouldn't rely on the implied/invisible behavior of changing $stdout
though, because that leads to REALLY confusing code. Instead, I'd strongly recommend using an explicit STDERR.puts
any time you're writing to STDERR and a bare puts
for normal output to STDOUT. If you're intermingling output to both, then it'd probably be clearer to use STDOUT.puts
and STDERR.puts
, but that's your call.
Now, IRB is the same as a regular script running in the interpreter is, as far as using $stdout
so writing output in IRB to $stdout
works the same:
irb(main):001:0> $stdout
#<IO:<STDOUT>>
irb(main):002:0> $stderr
#<IO:<STDERR>>
And:
irb(main):003:0> $stdout.puts 'foo'
foo
nil
irb(main):004:0> $stderr.puts 'foo'
foo
nil
And finally:
irb(main):007:0> $stdout.isatty
true
irb(main):008:0> $stdout.isatty
true
We can't really tell any difference until we look a little lower; They're both TTY channels, with the standard STDOUT and STDERR channel numbers:
irb(main):009:0> $stdout.fileno
1
irb(main):010:0> $stderr.fileno
2
Hopefully that helps 'splain it.
I just realized that IRB's reporting of the return value of puts
might be confusing you, causing you to think that the STDOUT is changing. That nil
is returned has nothing to do with STDOUT or STDERR. It's because puts
returns nil, which is dutifully reported by IRB.