2010-12-16 9 views
43

Ich möchte stderr vorübergehend für die Dauer eines Blocks in ein Ruby-Skript umleiten und sicherstellen, dass ich es am Ende der Blockierung auf seinen ursprünglichen Wert zurücksetze.Wie kann ich Stderr vorübergehend in Ruby umleiten?

Ich hatte Probleme zu finden, wie dies in den Ruby-Dokumenten zu tun.

+1

Verwandte Frage: http://stackoverflow.com/questions/3018595/how-do -i-redirect-stderr-und-stdout-zu-Datei-für-ein-Ruby-Skript –

Antwort

61

In Ruby $stderr bezieht sich auf den Ausgangsstrom, der zur Zeit als Stderr verwendet wird, während der Standard-STDERR Stderr Strom ist. Es ist einfach, vorübergehend einen anderen Ausgabestrom $stderr zuzuweisen.

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 

Jetzt können Sie folgendes tun:

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

captured_output 
#=> "test\n" 

Das gleiche Prinzip funktioniert auch für $stdout und STDOUT.

+0

Ich verwende dies auf eine Funktion (RubyVM :: InstructionSequence), die direkt in stderr (Dateideskriptor 2) in C schreibt. Wird das funktionieren? Ich versuche, die Stderr-Ausgabe einer Low-Level-C-Funktion zu unterdrücken, die von der Ruby-Laufzeit – horseyguy

+1

@banister aufgerufen wird: Ja, es sollte funktionieren; Ruby verwendet '$ stderr' intern. Und wenn es aus irgendeinem Grund nicht der Fall ist (was ein Fehler wäre), dann werden Sie leider nicht in der Lage sein, etwas in Ihrem Ruby-Code zu tun. – molf

+0

es funktioniert! wunderbar! :) danke – horseyguy

10

im Wesentlichen die gleiche wie @ molf Antwort, und hat die gleiche Nutzung:

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

Es StringIO verwendet sehr leicht prägnanter und erhält $ stderr als was auch immer es war, bevor capture_stderr genannt wurde. Hier

+0

Gute Verbesserungen! – molf

14

ist eine abstraktere Lösung (Kredit geht an 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 

Verbrauch:

silence_streams(STDERR) { do_something } 
+0

Mein RUBY_PLATFORM == "i386-mingw32", also würde dies auf meinem Windows-Rechner nicht funktionieren. Sie wissen nie, welche seltsamen Umgebungen die Leute ausführen, daher würde ich den String-Abgleich dafür empfehlen. – Nossidge

5

Ich mag die StringIO Antworten. Aber wenn Sie einen externen Prozess aufrufen, und $stderr = StringIO.new nicht funktioniert, könnten Sie stderr aus in eine temporäre Datei schreiben:

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 
Verwandte Themen