2010-12-16 14 views
43

Me gustaría redirigir temporalmente stderr en un script de Ruby durante la duración de un bloque, asegurándome de que lo restablezca a su valor original al final del bloque.¿Cómo redirecciono temporalmente stderr en Ruby?

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

+1

pregunta relacionada: http://stackoverflow.com/questions/3018595/how-do -i-redirect-stderr-and-stdout-to-file-for-a-ruby-script –

Respuesta

61

En Ruby, $stderr se refiere a la corriente de salida que se actualmente utiliza como stderr, mientras que STDERR es la corriente stderr predeterminado. Es fácil asignar temporalmente una secuencia 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 usted puede hacer lo siguiente:

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

captured_output 
#=> "test\n" 

El mismo principio se puede aplicar a los $stdout y STDOUT.

+0

Estoy usando esto en una función (RubyVM :: InstructionSequence) que escribe directamente en stderr (descriptor de archivo 2) en C. ¿Esto funcionará para eso? Estoy tratando de suprimir la salida stderr de una función de bajo nivel C llamada por el tiempo de ejecución Ruby – horseyguy

+1

@banister: Sí, debería funcionar; Ruby usa '$ stderr' internamente. Y si por algún motivo no es el caso (lo que sería un error), desafortunadamente no podrás hacer nada al respecto en tu código de Ruby. – molf

+0

¡funciona! ¡maravilloso! :) gracias – horseyguy

10

En esencia, el mismo que el de @ molf respuesta, y tiene el mismo uso:

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

Utiliza StringIO muy poco más concisa, y preserva $ stderr como lo era antes capture_stderr fue llamado.

+0

¡Buenas mejoras! – molf

14

Aquí es una solución más abstracto (mérito es de 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 } 
+0

Mi RUBY_PLATFORM == "i386-mingw32", por lo que esto no funcionaría en mi máquina con Windows. Nunca se sabe qué entornos extraños pueden estar funcionando, por lo que recomendaría que no coincidan las cadenas para esto. – Nossidge

5

me gustan las respuestas StringIO. Pero si usted está llamando a un proceso externo, y $stderr = StringIO.new no funciona, puede escribir stderr a 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 
Cuestiones relacionadas