В чем разница между STDIN и $stdin в Ruby?

Ruby имеет два способа обращения к стандартному вводу: константа STDIN и глобальная переменная $stdin.

Помимо того, что я могу назначить другой объект IO для $stdin, потому что он не является константой (например, перед форкированием для перенаправления ввода-вывода у моих детей), какая разница между STDIN и $stdin? Когда я должен использовать каждый в своем коде?

Если я переназначаю $stdin, это повлияет на STDIN?

И это также относится к STDOUT/$stdout и STDER/$stderr?

Ответ 1

Если $stdin переназначается, STDIN не влияет. Аналогично $stdin не изменяется, когда STDIN переназначается (что вполне возможно (хотя и бессмысленно), но выдает предупреждение). Однако, если ни одна переменная не была переназначена, они оба указывают на один и тот же объект ввода-вывода, поэтому вызов reopen ¹ на одном влияет на другой.

Все встроенные методы ruby ​​используют $< (a.k.a. ARGF) для чтения ввода. Если ARGV пусто, ARGF читается из $stdin, поэтому, если вы переназначаете $stdin, это повлияет на все встроенные методы. Если вы переназначаете STDIN, это не будет иметь эффекта, если какой-либо сторонний метод использует STDIN.

В вашем собственном коде вы должны использовать $stdin, чтобы он соответствовал встроенным методам².

¹ reopen - это метод, который может перенаправлять объект IO на другой поток или файл. Однако вы не можете использовать его для перенаправления ввода-вывода в StringIO, поэтому он не устраняет все случаи использования переназначения $stdin.

² Вы можете, конечно, также использовать $</ARGF, чтобы быть более совместимым со встроенными методами, но большую часть времени вы не хотите поведения ARGF, если вы явно используете поток stdin.

Ответ 2

STDERR и $stderr сначала указывают на одно и то же; вы можете переназначить глобальную переменную, но вы не должны связываться с константой. Парады $stdin и STDIN, $stdout и STDOUT аналогичны.

Мне пришлось несколько раз менять STDERR как альтернативу обезглавливанию некоторых камней, выводящих сообщения об ошибках с помощью STDERR.puts. Если вы переназначаете STDERR = $stdout, вы получите предупреждение, в то время как STDERR.reopen( "nul", "w" ), само собой разумеется.