Почему вывод STDERR из Ruby идет раньше, чем вывод STDOUT при перенаправлении? - PullRequest
6 голосов
/ 18 января 2012

В bash это дает вывод в ожидаемом порядке:

ruby -e "puts 'one'; raise 'two'"
one
-e:1:in `<main>': two (RuntimeError)

Но если я перенаправлю STDERR в STDOUT, я получу ошибку перед выводом, которую я не хочу:

ruby -e "puts 'one'; raise 'two'" 2>&1 | cat
-e:1:in `<main>': two (RuntimeError)
one

Я хочу перенаправить вывод в текстовый файл (он ведет себя так же, как cat выше) и получить и выходные данные, и исключения, но в том же порядке, что и при просмотре вывода в моем терминале. Можно ли этого достичь?

Ответы [ 4 ]

9 голосов
/ 18 января 2012

Это происходит из-за буферизации строк по сравнению с буферизацией блоков.Вы можете контролировать тип буферизации, вы можете очищать их в том месте, где вы хотите синхронизировать их вывод, или вы можете просто ждать выхода, после чего все сбрасывается.Если вы не форсируете его тем или иным образом, буферизация зависит от того, является ли вывод tty-типом 1 дескриптором файла, поэтому перенаправление в канал изменяет режим.

В частности:

                 true          false 
              ------------- --------------
$stdout.tty?  line-buffered block-buffered
$stderr.tty?  line-buffered line-buffered

Их можно настроить одинаково с помощью:

$stdout.sync = $stderr.sync = true # or false, of course

Мой тестовый пример:

$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


1.Смотрите ttyname (3).
2 голосов
/ 18 января 2012

Это потому, что STDOUT не всегда выводит сразу, чтобы заставить его выводить, вы используете IO#flush:

puts "one"
$>.flush

С другой стороны, STDERR всегда выводит немедленно.

0 голосов
/ 25 марта 2014

ruby -e STDOUT.sync=true -e "puts 'one'; raise 'two'" 2>&1 | cat

должен это сделать

0 голосов
/ 18 января 2012

Основываясь на ответах Маурисио и Гира Лавс Такоса, я придумал это (через Как включить STDOUT.sync в ruby ​​из командной строки ):

ruby -r "/tmp/sync.rb" -e "puts 'one'; raise 'two'" 2>&1 | cat
one
-e:1:in `<main>': two (RuntimeError)

, где /tmp/sync.rb содержит

STDOUT.sync=true

Или, если вы можете изменить сам скрипт, добавьте эту строку в начало.

Спасибо!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...