How do I temporarily redirect stderr in Ruby?

核能气质少年 提交于 2019-11-26 08:11:46

问题


I\'d like to temporarily redirect stderr in a Ruby script for the duration of a block, ensuring that I reset it to its original value at the end of the block.

I had trouble finding how to do this in the ruby docs.


回答1:


In Ruby, $stderr refers to the output stream that is currently used as stderr, whereas STDERR is the default stderr stream. It is easy to temporarily assign a different output stream to $stderr.

require "stringio"

def capture_stderr
  # The output stream must be an IO-like object. In this case we capture it in
  # an in-memory IO object so we can return the string value. You can assign any
  # IO object here.
  previous_stderr, $stderr = $stderr, StringIO.new
  yield
  $stderr.string
ensure
  # Restore the previous value of stderr (typically equal to STDERR).
  $stderr = previous_stderr
end

Now you can do the following:

captured_output = capture_stderr do
  # Does not output anything directly.
  $stderr.puts "test"
end

captured_output
#=> "test\n"

The same principle also works for $stdout and STDOUT.




回答2:


Here is a more abstract solution (credit goes to David Heinemeier Hansson):

def silence_streams(*streams)
  on_hold = streams.collect { |stream| stream.dup }
  streams.each do |stream|
    stream.reopen(RUBY_PLATFORM =~ /mswin/ ? 'NUL:' : '/dev/null')
    stream.sync = true
  end
  yield
ensure
  streams.each_with_index do |stream, i|
    stream.reopen(on_hold[i])
  end
end

Usage:

silence_streams(STDERR) { do_something }



回答3:


Essentially the same as @molf's answer, and has the same usage:

require "stringio"
def capture_stderr
  real_stderr, $stderr = $stderr, StringIO.new
  yield
  $stderr.string
ensure
  $stderr = real_stderr
end

It uses StringIO very slightly more concisely, and preserves $stderr as whatever it was before capture_stderr was called.




回答4:


I like the StringIO answers. But if you are calling an external process, and $stderr = StringIO.new doesn't work, you might write stderr out to a temporary file:

require 'tempfile'

def capture_stderr
  backup_stderr = STDERR.dup
  begin
    Tempfile.open("captured_stderr") do |f|
      STDERR.reopen(f)
      yield
      f.rewind
      f.read
    end
  ensure
    STDERR.reopen backup_stderr
  end
end


来源:https://stackoverflow.com/questions/4459330/how-do-i-temporarily-redirect-stderr-in-ruby

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!