Я хотел бы временно перенаправить stderr в Ruby script в течение всего блока, гарантируя, что я reset его исходное значение в конце блока.
Мне не удалось найти, как это сделать в рубиновых документах.
Я хотел бы временно перенаправить stderr в Ruby script в течение всего блока, гарантируя, что я reset его исходное значение в конце блока.
Мне не удалось найти, как это сделать в рубиновых документах.
В Ruby $stderr
ссылается на выходной поток, который используется в настоящее время как stderr, тогда как STDERR
- это поток st по умолчанию. Легко временно назначить другой выходной поток $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
.
Вот более абстрактное решение (заслуга Дэвида Хайннемайера Хансона):
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