41

Ruby has two ways of referring to the standard input: The STDIN constant , and the $stdin global variable.

Aside from the fact that I can assign a different IO object to $stdin because it's not a constant (e.g. before forking to redirect IO in my children), what's the difference between STDIN and $stdin? When should I use each in my code?

If I reassign $stdin, does it affect STDIN?

And does this also apply to STDOUT/$stdout and STDER/$stderr?

the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168

2 Answers2

44

If $stdin is reassigned, STDIN is not affected. Likewise $stdin is not affected when STDIN is reassigned (which is perfectly possible (though pointless), but will produce a warning). However if neither variable has been reassigned, they both point to the same IO object, so calling reopen¹ on one will affect the other.

All the built-in ruby methods use $< (a.k.a. ARGF) to read input. If ARGV is empty, ARGF reads from $stdin, so if you reassign $stdin, that will affect all built-in methods. If you reassign STDIN it will have no effect unless some 3rd party method uses STDIN.

In your own code you should use $stdin to be consistent with the built-in methods².

¹ reopen is a method which can redirect an IO object to another stream or file. However you can't use it to redirect an IO to a StringIO, so it does not eliminate all uses cases of reassigning $stdin.

² You may of course also use $</ARGF to be even more consistent with the built-in methods, but most of the time you don't want the ARGF behavior if you're explicitly using the stdin stream.

sepp2k
  • 363,768
  • 54
  • 674
  • 675
  • I'm pretty sure the built-in methods use `$<` (the default input stream) and `$>` (the default output stream) and neither `$stdin`/`STDIN` nor `$stdout`/`STDOUT`. In fact, that's pretty much the whole point of `$<` and `$>`: that you can redirect the input and output of methods such as `Kernel#puts` *without* affecting stdin/stdout. – Jörg W Mittag Nov 26 '10 at 12:10
  • 4
    @Jörg: You're right, that they use `$<` and `$>`, I'll correct that. But you're not right about your second point: `$>` and `$stdout` are aliases, so reassigning one will affect the other (unlike STDOUT which stays unaffected). `$<` is the same as `ARGF`, both of which *cannot* be reassigned. However reassigning `$stdin` *will* affect `$<` and `ARGF` because `ARGF` reads from `$stdin` if `ARGV` is empty. – sepp2k Nov 26 '10 at 12:55
1

STDERR and $stderr are pointing to the same thing initially; you can reassign the global variable but you shouldn't mess with the constant. $stdin and STDIN, $stdout and STDOUT pairs are likewise.

I had to change STDERR a couple of times as an alternative to monkey-patching some gems outputting error messages with STDERR.puts. If you reassign with STDERR = $stdout you get a warning while STDERR.reopen('nul', 'w') goes without saying.

mtelis
  • 654
  • 1
  • 6
  • 10