我有一个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.
tok*_*and 13
@wallyk是对的问题.一种解决方案是使用Signal.trap捕获信号:
Signal.trap("PIPE", "EXIT")
Run Code Online (Sandbox Code Playgroud)
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)
注意:
第1部分适用于设计用作基于终端的命令行实用程序的 Ruby脚本,假设它们在接收时不需要自定义处理或清理SIGPIPE,并假设您希望它们展示标准Unix实用程序的行为,例如cat,它们静默终止接收时带有特定的退出代码SIGPIPE.
第二部分适用于需要自定义处理的SIGPIPE脚本,例如显式清理和错误消息的(条件)输出.
SIGPIPE:如果您希望脚本显示系统的默认行为,就像大多数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.