Подтвердить что ты не робот

Сломанная труба (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")
4b9b3361

Ответ 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.