Сломанная труба (Errno:: EPIPE)
У меня появляется ошибка Broken pipe (Errno::EPIPE)
, и я не понимаю, что это такое или как ее исправить. полная ошибка:
example.rb:19:in `write': Broken pipe (Errno::EPIPE)
from example.rb:19:in `print'
from example.rb:19
строка 19 моего кода:
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
Ответы
Ответ 1
Это означает, что всякий раз, когда вывод печати на него больше не подключен. Предположительно, программа начиналась как вход в какую-то другую программу:
% ruby_program | another_program
Случилось так, что another_program
вышло раньше, чем print
.
Ответ 2
@wallyk прав на проблему. Одним из решений является захват сигнала с помощью Signal.trap:
Signal.trap("PIPE", "EXIT")
Ответ 3
Хотя сигнальные ловушки действительно работают, как сказал tokland, они определены в широком масштабе приложения и могут вызвать какое-то неожиданное поведение, если вы хотите обрабатывать сломанную трубу каким-то другим способом в другом месте вашего приложения.
Я бы предложил просто использовать стандартное спасение, так как ошибка все еще наследуется от StandardError. Подробнее об этом модуле ошибок: http://ruby-doc.org/core-2.0.0/Errno.html
Пример:
begin
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
rescue Errno::EPIPE
puts "Connection broke!"
end
Изменить: важно отметить (как @mklement0 делает в комментариях), что если вы изначально отправляете свой вывод, используя puts для чего-то ожидающего вывода на STDOUT, последний, помещенный в код выше, поднимет еще одно исключение Errno :: EPIPE. Вероятно, лучше использовать STDERR.puts в любом случае.
begin
vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
rescue Errno::EPIPE
STDERR.puts "Connection broke!"
end
Ответ 4
Замечания:
-
Первый раздел применяется к скриптам Ruby, предназначенным для работы в качестве утилит командной строки на терминале, предполагая, что они не требуют специальной обработки или очистки при получении SIGPIPE
и предполагают, что вы хотите, чтобы они отображали поведение стандартных утилит Unix, таких как cat
, которые при получении SIGPIPE
прекратите работу с помощью специального кода выхода.
-
Второй раздел предназначен для сценариев, требующих специальной обработки SIGPIPE
, таких как явная очистка и (условный) вывод сообщений об ошибках.
Выбор режима работы SIGPIPE
умолчанию:
Чтобы дополнить wallyk полезным ответом и tokland полезным ответом:
Если вы хотите, чтобы ваш скрипт отображал поведение по умолчанию в системе, как это делают большинство утилит Unix (например, cat
), используйте
Signal.trap("SIGPIPE", "SYSTEM_DEFAULT")
в начале вашего скрипта.
Теперь, когда ваш скрипт получает сигнал SIGPIPE
(в Unix-подобных системах), поведение по умолчанию в системе будет:
- спокойно прекратите свой скрипт
- код выхода отчета
141
(который вычисляется как 128
(с указанием окончания по сигналу) + 13
(номер SIGPIPE
))
(В отличие от этого Signal.trap("PIPE", "EXIT")
будет сообщать код выхода 0
при приеме сигнала, что указывает на успех.)
Обратите внимание, что в контексте оболочки код выхода часто не ruby examble.rb | head
в команде, такой как ruby examble.rb | head
ruby examble.rb | head
, потому что оболочка (по умолчанию) сообщает только последний код выхода команды.
В bash
вы можете изучить ${PIPESTATUS[@]}
чтобы увидеть коды выхода всех команд в конвейере.
Минимальный пример (run from bash
):
ruby -e "Signal.trap('PIPE','SYSTEM_DEFAULT');(1..1e5).each do|i| puts i end" | head
Код Ruby пытается вывести 100 000 строк, но head
только выводит первые 10 строк и затем выходит, что закрывает считываемый конец канала, который соединяет две команды.
В следующий раз, когда код Ruby попытается записать конец этого сломанного канала (после заполнения буфера конвейера), он вызывает сигнал SIGPIPE
, который завершает процесс Ruby спокойно, с кодом выхода 141
, который вы можете проверить с помощью echo ${PIPESTATUS[0]}
.
В отличие от этого, если вы удалили Signal.trap('PIPE','SYSTEM_DEFAULT')
, то есть с поведением Ruby по умолчанию, команда будет шумно разбиваться (несколько строк вывода stderr), а код выхода будет незарегистрированным 1
.
Пользовательская обработка SIGPIPE
:
Ниже приводится подробный ответ на полезный ответ donovan.lampa и добавляется усовершенствование, предложенное Киммо Лехто, который указывает, что в зависимости от цели вашего скрипта получение SIGPIPE
не всегда должно заканчиваться спокойно, поскольку оно может указывать на допустимое условие ошибки, особенно в сети код, такой как код для загрузки файла из Интернета.
Он рекомендует следующую идиому для этого сценария:
begin
# ... The code that could trigger SIGPIPE
rescue Errno::EPIPE
# ... perform any cleanup, logging, ... here
# Raise an exception - which translates into stderr output -
# but only when outputting directly to a terminal.
# That way, failure is quiet inside a pipeline, such as when
# piping to standard utility 'head', where SIGPIPE is an expected
# condition.
raise if $stdout.tty?
# If the stack trace that the 'raise' call results in is too noisy
# use something like the following instead, which outputs just the
# error message itself to stderr:
# $stderr.puts $! if $stdout.tty?
# Or, even simpler:
# warn $! if $stdout.tty?
# Exit with the usual exit code that indicates termination by SIGPIPE
exit 141
end
Как однострочный:
... rescue Errno::EPIPE raise if $stdout.tty?; exit 141
Примечание: Rescuing Errno::EPIPE
работает, потому что, если сигнал игнорируется, запись системного вызова в конвейер возвращается к вызывающему абоненту (вместо завершения процесса вызова вызывающего абонента), а именно со стандартным кодом ошибки EPIPE
, какие поверхности Ruby являются исключением Errno::EPIPE
.