【发布时间】:2011-05-26 10:49:51
【问题描述】:
我想在一个块的持续时间内临时重定向 Ruby 脚本中的 stderr,确保我在块结束时将其重置为其原始值。
我在 ruby 文档中找不到如何执行此操作。
【问题讨论】:
我想在一个块的持续时间内临时重定向 Ruby 脚本中的 stderr,确保我在块结束时将其重置为其原始值。
我在 ruby 文档中找不到如何执行此操作。
【问题讨论】:
在 Ruby 中,$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
现在您可以执行以下操作:
captured_output = capture_stderr do
# Does not output anything directly.
$stderr.puts "test"
end
captured_output
#=> "test\n"
同样的原则也适用于$stdout 和STDOUT。
【讨论】:
$stderr。如果由于某种原因并非如此(这将是一个错误),那么不幸的是,您将无法在 Ruby 代码中对其进行任何处理。
StringIO 库?
$stderr 的值的情况下不起作用。相反,reopen 将适用于$stderr 的所有副本。问题是将StringIO 传递给reopen 不起作用,我收到此错误:“TypeError: no implicit conversion of StringIO into String”。
这是一个更抽象的解决方案(感谢 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
用法:
silence_streams(STDERR) { do_something }
【讨论】:
与@molf 的回答基本相同,用法相同:
require "stringio"
def capture_stderr
real_stderr, $stderr = $stderr, StringIO.new
yield
$stderr.string
ensure
$stderr = real_stderr
end
它使用 StringIO 更加简洁,并保留 $stderr 与调用 capture_stderr 之前一样。
【讨论】:
我喜欢 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
【讨论】: