Вывод Ruby в консоли Windows VS mingw64 VS cygwin64

У меня действительно странная проблема. Вот простой код, использующий puts:

puts "Dale"
sleep 1
puts "Cooper"

У меня есть 3 разных терминала/консоли:

  • Консоль Windows (по умолчанию в системе)
  • mingw64 (UNIX-подобный терминал, установленный вместе с Git)
  • cygwin64 (UNIX-подобный терминал)

Вот странная вещь: код работает как положено только в стандартной консоли Windows. UNIX-подобные терминалы ждут 1 секунду и только потом показывают вывод (обе строки одновременно). По сути, UNIX-подобные терминалы ждут выхода программы, а затем показывают окончательный результат вывода.

Если я заменю puts на print, это не повлияет на процесс выполнения. UNIX-подобные терминалы по-прежнему будут задерживать вывод до тех пор, пока программа не завершит работу.

Но следующие два примера работают правильно во всех 3-х терминалах/консолях:

system("echo Dale")
sleep 1
system("echo Cooper")

Этот добавляет кавычки, но в остальном код работает так, как ожидалось.

p "Dale"
sleep 1
p "Cooper"

Сказав это, я предполагаю, что это как-то связано с Ruby. Пробовал разные версии Ruby.

Может кто-нибудь объяснить, почему это происходит и каковы возможные способы обойти эту проблему?


person MOPO3OB    schedule 02.03.2018    source источник
comment
Как правило, с альтернативными терминалами они фактически используют именованный канал для стандартного ввода-вывода для связи с терминальным приложением. Многие приложения видят, что стандартный ввод-вывод — это канал, и переключаются в режим полной буферизации вместо линейной буферизации. Консоль Windows, с другой стороны, использует файлы для устройства ConDrv, которое является символьным устройством, и, таким образом, приложения используют буферизацию строк или не буферизуют ее, когда стандартный ввод-вывод является консолью.   -  person Eryk Sun    schedule 02.03.2018
comment
Интерпретатор Ruby может поддерживать параметры командной строки или переменные среды для управления буферизацией, когда стандартным вводом-выводом является канал или файл на диске.   -  person Eryk Sun    schedule 02.03.2018
comment
@eryksun мудрое редактирование, спасибо. Что посоветуете сделать с буферизацией?   -  person MOPO3OB    schedule 03.03.2018
comment
Обычно доступны варианты без буферизации, буферизация строк (т. е. сбрасывание при записи LF или CRLF) или полная буферизация (например, буфер размером 4 КБ, который может не сбрасываться до завершения процесса, если записано немного).   -  person Eryk Sun    schedule 03.03.2018
comment
@eryksun спасибо за помощь! В принципе вы ответили на мой вопрос! ^^   -  person MOPO3OB    schedule 03.03.2018


Ответы (1)


Вот я отвечаю на свой вопрос.

Маленький фон

Если вы сделаете puts STDOUT.sync перед кодом, вы увидите, что независимо от того, используете ли вы консоль Windows или UNIX-подобный терминал, он скажет, что для STDOUT.sync установлено значение false. Это странно, потому что консоль Windows сразу сбрасывает вывод, а UNIX-подобные терминалы - нет. Я не уверен, почему это происходит.

Решение

Вы можете выполнить STDOUT.flush (или $stdout.flush), чтобы сбросить буфер, или установить для STDOUT.sync (или $stdout.sync) значение true. Оба варианта полностью совместимы с консолью Windows. Таким образом, код будет следующим:

puts "Dale"
STDOUT.flush
sleep 1
puts "Cooper"

или больше рекомендуется:

STDOUT.sync = true
puts "Dale"
sleep 1
puts "Cooper"

Определение того, когда это консоль Windows или UNIX-подобный терминал

Вот небольшой трюк, предложенный @eryksun, чтобы узнать, выполняется ли код в консоли Windows. или UNIX-подобный терминал. STDOUT.isatty работает как бы в перевернутом виде при запуске под Windows, но, тем не менее, помогает.

if STDOUT.isatty
    # Windows console
else
    # UNIX-like terminal
end

Имейте в виду, что это предполагает, что вы уже знаете, что код запускается под Windows. Хороший способ проверить ОС описан здесь.

использованная литература

Основной источник ответа можно найти здесь. Идея ответа принадлежит @eryksun.

STDOUT.sync, STDOUT.sync = (вопрос об этом методе), STDOUT.flush, STDOUT.isatty.

person MOPO3OB    schedule 02.03.2018
comment
Для Windows доступно множество версий bash, включая bash WSL для Windows 10. Наличие bash в PATH не означает, что вы используете UNIX-подобный терминал. Я думаю, что существенная разница заключается в том, рассматривается ли stdout как файл диска / канала или символьный файл. Иногда последний называется tty, даже в Windows, хотя в Windows нет устройств tty/pty. Найдите такой метод, как isatty. - person Eryk Sun; 03.03.2018
comment
@eryksun ты прав. Хотя довольно забавно, что STDOUT.isatty возвращает true на консоли Windows и false на UNIX-подобных терминалах. В любом случае, это более надежный метод, на который можно положиться. Еще раз, спасибо. - person MOPO3OB; 03.03.2018
comment
Ожидается, что isatty будет ложным для UNIX-подобных терминалов в Windows. Они вынуждены использовать именованные каналы для стандартных дескрипторов, поскольку в Windows нет устройств UNIX pty/tty. - person Eryk Sun; 03.03.2018
comment
@eryksun спасибо, что прояснили это. Обновил ответ. - person MOPO3OB; 03.03.2018