Skip to content

Instantly share code, notes, and snippets.

@Sutto
Created June 2, 2011 01:13
Show Gist options
  • Save Sutto/1003733 to your computer and use it in GitHub Desktop.
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...
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