¿Cómo puedo redirigir temporalmente stderr en Ruby?


Me gustaría redirigir temporalmente stderr en un script Ruby durante la duración de un bloque, asegurándome de restablecerlo a su valor original al final del bloque.

Tuve problemas para encontrar cómo hacer esto en los documentos de ruby.

 43
Author: Andrew Grimm, 2010-12-16

4 answers

En Ruby, $stderrse refiere al flujo de salida que es actualmente utilizado como stderr, mientras que STDERRes el flujo predeterminado stderr. Es fácil asignar temporalmente un flujo de salida diferente a $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

Ahora puedes hacer lo siguiente:

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

captured_output
#=> "test\n"

El mismo principio también funciona para $stdout y STDOUT.

 62
Author: molf,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-16 11:17:10

Aquí hay una solución más abstracta (el crédito es para 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

Uso:

silence_streams(STDERR) { do_something }
 15
Author: user2398029,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-01-22 06:42:12

Esencialmente lo mismo que la respuesta de @molf, y tiene el mismo uso:

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

Usa StringIO de forma un poco más concisa, y conserva $stderr como lo que fuera antes de que capture_stderr fuera llamado.

 10
Author: gunn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-12-27 16:42:01

Me gustan las respuestas StringIO. Pero si está llamando a un proceso externo, y $stderr = StringIO.new no funciona, puede escribir stderr en un archivo temporal:

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
 5
Author: zhon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-10 16:20:54