如何在Ruby中临时重定向stderr?

hor*_*guy 43 ruby stderr

我想暂时在一个块的持续时间内将stderr重定向到Ruby脚本中,确保在块结束时将其重置为原始值.

我很难在ruby文档中找到如何做到这一点.

mol*_*olf 63

在Ruby中,$stderr指的是输出流目前使用的作为标准错误,而STDERR默认 stderr的流.可以很容易地临时分配不同的输出流$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
Run Code Online (Sandbox Code Playgroud)

现在您可以执行以下操作:

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

captured_output
#=> "test\n"
Run Code Online (Sandbox Code Playgroud)

同样的原则也适用于$stdoutSTDOUT.


use*_*029 17

这是一个更抽象的解决方案(归功于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
Run Code Online (Sandbox Code Playgroud)

用法:

silence_streams(STDERR) { do_something }
Run Code Online (Sandbox Code Playgroud)


gun*_*unn 10

基本上与@ molf的答案相同,具有相同的用法:

require "stringio"
def capture_stderr
  real_stderr, $stderr = $stderr, StringIO.new
  yield
  $stderr.string
ensure
  $stderr = real_stderr
end
Run Code Online (Sandbox Code Playgroud)

它更简洁地使用StringIO,并且在调用capture_stderr之前保留$ stderr.


zho*_*hon 6

我喜欢StringIO的答案.但是,如果您正在调用外部进程并且$stderr = StringIO.new不起作用,则可以将stderr写入临时文件:

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
Run Code Online (Sandbox Code Playgroud)