Разбитая труба (Errno :: EPIPE) - PullRequest
38 голосов
/ 27 ноября 2009

У меня появляется ошибка 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")

Ответы [ 4 ]

23 голосов
/ 27 ноября 2009

Это означает, что независимо от того, на какую печать выводится сообщение, больше не подключено. Предположительно, программа началась как ввод в другую программу:

 % ruby_program | another_program

Что случилось, так это то, что another_program завершился за некоторое время до print, о котором идет речь.

14 голосов
/ 15 марта 2013

@ Wallyk прав на проблему. Одним из решений является захват сигнала с помощью Signal.trap :

Signal.trap("PIPE", "EXIT")
13 голосов
/ 06 августа 2013

Хотя ловушки сигналов работают, как сказал Токланд, они определены для всего приложения и могут вызвать неожиданное поведение, если вы захотите обработать сломанный канал каким-либо другим способом в другом месте вашего приложения.

Я бы предложил использовать стандартное восстановление, поскольку ошибка все еще наследуется от 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 делает в комментариях), что если вы изначально передавали свои выходные данные с использованием путов к чему-то ожидающему вывод на 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
9 голосов
/ 07 мая 2015

Примечание:

  • 1-й раздел применяется к сценариям Ruby, предназначенным для работы в качестве служебных программ командной строки на основе терминала , при условии, что они не требуют никакой пользовательской обработки или очистка при получении SIGPIPE и при условии, что вы хотите, чтобы они демонстрировали поведение стандартных утилит Unix, таких как cat, которые тихо завершаются с специальный код выхода при получении SIGPIPE.

  • 2-й раздел предназначен для сценариев, для которых требуется пользовательская обработка из SIGPIPE, например явная очистка и (условная) вывод ошибки сообщения .


Выбор системы по умолчанию из SIGPIPE:

В дополнение к полезному ответу wallyk и полезному ответу tokland :

Если вы хотите, чтобы ваш скрипт демонстрировал системное поведение по умолчанию , так как большинство утилит Unix (например, cat) делают , используйте

Signal.trap("SIGPIPE", "SYSTEM_DEFAULT") 

в начале вашего сценария.

Теперь, когда ваш скрипт получает сигнал SIGPIPE (в Unix-подобных системах), поведение системы по умолчанию будет:

  • тихо прекратить ваш скрипт
  • отчет код выхода 141 (который рассчитывается как 128 (указывает на завершение сигналом ) + 13 (SIGPIPE * число *) 1082 *))

(В отличие от этого Signal.trap("PIPE", "EXIT") сообщит код выхода 0 при получении сигнала, который указывает success .)

Обратите внимание, что в контексте shell код выхода часто не виден в такой команде, как ruby examble.rb | head, поскольку оболочка (по умолчанию) сообщает только о выходе команды last код.

В bash вы можете проверить ${PIPESTATUS[@]}, чтобы увидеть коды выхода всех команд в конвейере.


Минимальный пример (запуск от 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

Примечание: Спасение Errno::EPIPE работает, потому что, если сигнал игнорируется, запись системного вызова в конвейер возвращается к вызывающей стороне (вместо процесса вызывающей стороны прекращается ) а именно со стандартной ошибкой код EPIPE, которую Ruby выдает как исключение Errno::EPIPE.

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