破管(Errno :: EPIPE)

Ton*_*ark 40 ruby

我有一个Broken pipe (Errno::EPIPE)错误弹出,我不明白它是什么或如何解决它.完整的错误是:

example.rb:19:in `write': Broken pipe (Errno::EPIPE)
    from example.rb:19:in `print'
    from example.rb:19
Run Code Online (Sandbox Code Playgroud)

我的代码的第19行是:

vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
Run Code Online (Sandbox Code Playgroud)

wal*_*lyk 25

这意味着输出的任何连接打印都不再连接.据推测,该计划开始作为其他一些计划的输入:

 % ruby_program | another_program
Run Code Online (Sandbox Code Playgroud)

发生的事情是在问题发生another_program前的某个时间已经退出print.

  • @sepiroth:`EPIPE`是_system_-defined; 它是由系统调用报告的退出代码,它触发了`SIGPIPE`信号,这通常表示_pipe_的_reading_端的进程已经退出(而_writing_ end仍在尝试写入管道); 此外,在_network_上下文中,[this](https://www.gnu.org/software/libc/manual/html_mono/libc.html#Operation-Error-Signals)指出:"SIGPIPE`的另一个原因是您尝试输出到未连接的_socket_.请参阅[发送数据](https://www.gnu.org/software/libc/manual/html_mono/libc.html#Sending═)". (2认同)

tok*_*and 13

@wallyk是对的问题.一种解决方案是使用Signal.trap捕获信号:

Signal.trap("PIPE", "EXIT")
Run Code Online (Sandbox Code Playgroud)

  • 这很方便,但请注意,Ruby 进程的退出代码将为“0”,而不是通常的“141”(尽管这可能无关紧要,取决于用例) (2认同)

don*_*mpa 13

尽管信号陷阱确实有效,但正如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
Run Code Online (Sandbox Code Playgroud)

编辑:重要的是要注意(正如@ 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
Run Code Online (Sandbox Code Playgroud)

  • @ mklement0您实际上正在抢救异常,但是在您的救援语句中再次引发异常,因为您正在尝试将自定义异常字符串写入STDOUT,该字符串仍然被管道传输(并且其管道已经被破坏).如果你运行这个'ruby -e'开始; puts(1..10000).map {| n | "line#{n}"}; 救援Errno :: EPIPE; STDERR.puts"不能做:#{$!.message}"; 结束'| 你会看到它确实拯救了原来的Errno :: EPIPE例外. (3认同)

mkl*_*nt0 9

注意:

  • 第1部分适用于设计用作基于终端的命令行实用程序的 Ruby脚本,假设它们在接收时不需要自定义处理或清理SIGPIPE,并假设您希望它们展示标准Unix实用程序的行为,例如cat,它们静默终止接收时带有特定的退出代码SIGPIPE.

  • 第二部分适用于需要自定义处理SIGPIPE脚本,例如显式清理错误消息的(条件)输出.


选择加入到系统的默认处理SIGPIPE:

为补充wallyk的有用答案tokland的有用答案:

如果您希望脚本显示系统的默认行为,就像大多数Unix实用程序(例如cat)一样,请使用

Signal.trap("SIGPIPE", "SYSTEM_DEFAULT") 
Run Code Online (Sandbox Code Playgroud)

在脚本的开头.

现在,当您的脚本收到SIGPIPE信号时(在类Unix系统上),系统的默认行为将:

  • 悄悄地终止你的脚本
  • 报告退出代码141(计算为128(表示按信号终止)+ 13(SIGPIPE号码))

(相反,在收到信号时Signal.trap("PIPE", "EXIT")会报告退出代码0,表示成功.)

请注意,在shell上下文中,退出代码在命令中通常不明显ruby examble.rb | head,因为shell(默认情况下)仅报告最后一个命令的退出代码.

bash,您可以检查${PIPESTATUS[@]}以查看管道中所有命令的退出代码.


最小的例子(从...开始bash):

ruby -e "Signal.trap('PIPE','SYSTEM_DEFAULT');(1..1e5).each do|i| puts i end" | head
Run Code Online (Sandbox Code Playgroud)

Ruby代码尝试输出100,000行,但head只输出前10行然后退出,这将关闭连接两个命令的管道的读取端.

下一次Ruby代码尝试写入现在已经断开的管道的写入端(在填满管道缓冲区之后),它会触发signal SIGPIPE,它会使用退出代码安静地终止Ruby进程,之后141您可以验证echo ${PIPESTATUS[0]}.

相比之下,如果你删除了Signal.trap('PIPE','SYSTEM_DEFAULT'),即使用Ruby的默认行为,命令会大声破坏(几行stderr输出),退出代码将是非描述性的1.


定制处理SIGPIPE:

以下内容基于donovan.lampa的有用答案,并添加了Kimmo Lehto建议的改进 ,他指出,根据您的脚本的目的,接收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
Run Code Online (Sandbox Code Playgroud)

作为单线:

... rescue Errno::EPIPE raise if $stdout.tty?; exit 141
Run Code Online (Sandbox Code Playgroud)

注意:救援Errno::EPIPE工作,因为如果信号被忽略,系统调用写入管道返回给调用者(而不是调用者进程终止),即使用标准错误代码 EPIPE,Ruby表示为异常Errno::EPIPE.