Created
June 2, 2011 01:13
-
-
Save Sutto/1003733 to your computer and use it in GitHub Desktop.
A simple(?) way to capture stderr and stdout without losing them. Not 100% perfect...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'tempfile' | |
class IOMirror | |
attr_accessor :original_stdout, :original_stderr, :mirror_stdout, :mirror_stderr | |
def initialize(mirror_stdout, mirror_stderr) | |
@mirror_stdout = mirror_stdout | |
@mirror_stderr = mirror_stderr | |
@sync_mapping = {} | |
end | |
def redirect! | |
@original_stdout = IO.new(STDOUT.fileno) | |
@original_stderr = IO.new(STDERR.fileno) | |
@tail_stdout_pid = fork { exec "tail -f '#{mirror_stdout.path}'" } | |
@tail_stderr_pid = fork { exec "tail -f '#{mirror_stderr.path}' 1>&2" } | |
reopen_descriptors mirror_stdout, mirror_stderr | |
end | |
def revert! | |
reopen_descriptors original_stdout, original_stderr | |
cleanup_pid! :tail_stdout_pid | |
cleanup_pid! :tail_stderr_pid | |
end | |
def with_redirection | |
redirect! | |
yield if block_given? | |
ensure | |
revert! | |
# Time for the Kernel to flush both items. | |
sleep 0.5 | |
end | |
def cleanup_pid!(name) | |
value = instance_variable_get("@#{name}") | |
if value | |
Process.kill :QUIT, value | |
instance_variable_set "@#{name}", nil | |
end | |
end | |
def reopen_descriptors(out, err) | |
restore_sync! STDOUT | |
restore_sync! STDERR | |
capture_sync! out | |
capture_sync! err | |
no_warn do | |
$stdout = out | |
Object.const_set :STDOUT, out | |
$stderr = err | |
Object.const_set :STDERR, err | |
end | |
end | |
def capture_sync!(io) | |
@sync_mapping[io] = io.sync | |
io.flush | |
io.sync = true | |
end | |
def restore_sync!(io) | |
if @sync_mapping.has_key?(io) | |
io.sync = @sync_mapping[io] | |
@sync_mapping.delete io | |
end | |
io.flush | |
end | |
# Suppress warnings, most useful for constant redefinition. | |
def no_warn | |
verbose = $VERBOSE | |
$VERBOSE = nil | |
yield | |
ensure | |
$VERBOSE = verbose | |
end | |
end | |
temporary_stdout = Tempfile.new('stdout') | |
temporary_stderr = Tempfile.new('stderr') | |
mirror = IOMirror.new(temporary_stdout, temporary_stderr) | |
mirror.with_redirection do | |
puts "Hello A" | |
$stdout.puts "Hello B" | |
STDOUT.puts "Hello C" | |
$stderr.puts "Hello D" | |
STDERR.puts "Hello E" | |
end | |
temporary_stdout.rewind | |
temporary_stderr.rewind | |
puts "STDOUT contained: #{temporary_stdout.read.inspect}" | |
puts "STDERR contained: #{temporary_stderr.read.inspect}" | |
temporary_stdout.close | |
temporary_stderr.close |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment