Как временно перенаправить stderr в Ruby? - PullRequest
43 голосов
/ 16 декабря 2010

Я бы хотел временно перенаправить stderr в сценарий Ruby на время блока, гарантируя, что я верну его исходное значение в конце блока.

У меня возникли проблемы с поиском, каксделайте это в рубиновых документах.

Ответы [ 4 ]

63 голосов
/ 16 декабря 2010

В Ruby $stderr относится к выходному потоку, который в настоящее время используется в качестве 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

Теперь вы можете делать следующее:

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

captured_output
#=> "test\n"

Тот же принцип работает и для $stdout и STDOUT.

16 голосов
/ 22 января 2012

Вот более абстрактное решение (заслуга Дэвида Хайнемайера Ханссона):

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 }
10 голосов
/ 16 декабря 2010

По сути то же самое, что и ответ @ molf, и имеет такое же использование:

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

Он использует StringIO немного более кратко и сохраняет $ stderr, каким он был до вызова capture_stderr.

6 голосов
/ 10 января 2014

Мне нравятся ответы 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
...