Broken pipe (Errno::EPIPE)

前端 未结 4 647
迷失自我
迷失自我 2020-12-05 04:05

i have a Broken pipe (Errno::EPIPE) error popping up and i don\'t understand what it is or how to fix it. the full error is:

example.rb:19:in `w         


        
相关标签:
4条回答
  • 2020-12-05 04:29

    @wallyk is right on the problem. One solution is to capture the signal with Signal.trap:

    Signal.trap("PIPE", "EXIT")
    

    If you are aware of some problem with this approach, please add a comment below.

    0 讨论(0)
  • 2020-12-05 04:32

    Note:

    • The 1st section applies to Ruby scripts designed to act as terminal-based command-line utilities, assuming they require no custom handling or cleanup on receiving SIGPIPE, and assuming that you want them to exhibit the behavior of standard Unix utilities such as cat, which terminate quietly with a specific exit code when receiving SIGPIPE.

    • The 2nd section is for scripts that require custom handling of SIGPIPE, such as explicit cleanup and (conditional) output of error messages.


    Opting into the system's default handling of SIGPIPE:

    To complement wallyk's helpful answer and tokland's helpful answer:

    If you want your script to exhibit the system's default behavior, as most Unix utilities (e.g., cat) do, use

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

    at the beginning of your script.

    Now, when your script receives the SIGPIPE signal (on Unix-like systems), the system's default behavior will:

    • quietly terminate your script
    • report exit code 141 (which is calculated as 128 (indicating termination by signal) + 13 (SIGPIPE's number))

    (By contrast, Signal.trap("PIPE", "EXIT") would report exit code 0, on receiving the signal, which indicates success.)

    Note that in a shell context the exit code is often not apparent in a command such as ruby examble.rb | head, because the shell (by default) only reports the last command's exit code.

    In bash, you can examine ${PIPESTATUS[@]} to see the exit codes of all commands in the pipeline.


    Minimal example (run from bash):

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

    The Ruby code tries to output 100,000 lines, but head only outputs the first 10 lines and then exits, which closes the read end of the pipe that connects the two commands.

    The next time the Ruby code tries to the write end of that now broken pipe (after filling up the pipeline buffer), it triggers signal SIGPIPE, which terminates the Ruby process quietly, with exit code 141, which you can verify with echo ${PIPESTATUS[0]} afterwards.

    By contrast, if you removed Signal.trap('PIPE','SYSTEM_DEFAULT'), i.e. with Ruby's default behavior, the command would break noisily (several lines of stderr output), and the exit code would be the nondescript 1.


    Custom handling of SIGPIPE:

    The following builds on donovan.lampa's helpful answer and adds an improvement suggested by Kimmo Lehto, who points out that, depending on your script's purpose, receiving SIGPIPE shouldn't always terminate quietly, because it may indicate a legitimate error condition, notably in network code such as code for downloading a file from the internet.
    He recommends the following idiom for that scenario:

    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
    

    As a one-liner:

    ... rescue Errno::EPIPE raise if $stdout.tty?; exit 141
    

    Note: Rescuing Errno::EPIPE works, because if the signal is ignored, the system call writing to the pipeline returns to the caller (instead of the caller process getting terminated), namely with standard error code EPIPE, which Ruby surfaces as exception Errno::EPIPE.

    0 讨论(0)
  • 2020-12-05 04:50

    Although signal traps do work, as tokland said, they are defined application wide and can cause some unexpected behavior if you want to handle a broken pipe in some other way somewhere else in your app.

    I'd suggest just using a standard rescue since the error still inherits from StandardError. More about this module of errors: http://ruby-doc.org/core-2.0.0/Errno.html

    Example:

    begin
      vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
    rescue Errno::EPIPE
      puts "Connection broke!"
    end
    

    Edit: It's important to note (as @mklement0 does in the comments) that if you were originally piping your output using puts to something expecting output on STDOUT, the final puts in the code above will raise another Errno::EPIPE exception. It's probably better practice to use STDERR.puts anyway.

    begin
      vari.print("x=" + my_val + "&y=1&z=Add+Num\r\n")
    rescue Errno::EPIPE
      STDERR.puts "Connection broke!"
    end
    
    0 讨论(0)
  • 2020-12-05 04:52

    It means that whatever connection print is outputting to is no longer connected. Presumably the program began as input to some other program:

     % ruby_program | another_program
    

    What's happened is that another_program has exited sometime before the print in question.

    0 讨论(0)
提交回复
热议问题