Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reassigning global $stdout to console - ruby

I am trying to set $stdout to write to a file temporarily and then back to a file.

test.rb :
   old_stdout = $stdout    
    $stdout.reopen("mytestfile.out",'w+')
       puts "this goes in mytestfile"
    $stdout= old_stdout
puts "this should be on the console"
    $stdout.reopen("mytestfile1.out",'w+')
       puts "this goes in mytestfile1:"
    $stdout = old_stdout
 puts "this should be back on the console"

Here is the output.

ruby test.rb => no output on the console
cat mytestfile.out 
  this goes in mytestfile
  this should be on the console
cat  mytestfile1.out
  this goes in mytestfile1:
  this should be back on the console

I am not sure why $stdout is not getting reset to console ?

like image 335
codeObserver Avatar asked Feb 13 '12 01:02

codeObserver


2 Answers

This problem can be resolved by calling dup on $stdout before changing it:

old_stdout = $stdout.dup  
$stdout.reopen("mytestfile.out",'w+')
puts "this goes in mytestfile"
$stdout = old_stdout.dup
puts "this should be on the console"
$stdout.reopen("mytestfile1.out",'w+')
puts "this goes in mytestfile1:"
$stdout = old_stdout
puts "this should be back on the console"

Output:

ruby test.rb
# => this should be on the console
# => this should be back on the console
cat mytestfile.out
# => this goes in mytestfile
cat mytestfile1.out
# => this goes in mytestfile1

Here's how I usually package this functionality into a function:

# Runs a block of code while blocking stdout.
# Note that /dev/null should be changed to NUL on Windows.
def silence_stdout(log = '/dev/null')
  old = $stdout.dup
  $stdout.reopen(File.new(log, 'w'))
  yield
  $stdout = old
end

Usage:

silence_stdout 'mytestfile.out' do
  puts "this goes in mytestfile"
end

puts "this should be on the console"

silence_stdout 'mytestfile1.out' do
  puts "this goes in mytestfile1"
end

puts "this should be back on the console"

Edit: as another poster mentioned, using reopen is only necessary when working with pure Ruby code. The function above works both with pure Ruby code and when using, for example, C extensions that write to STDOUT.

like image 115
user2398029 Avatar answered Nov 08 '22 12:11

user2398029


You don't need to use reopen if you're just using Ruby code. puts and other Ruby methods will use the current value of $stdout so you can just reassign it.

old_stdout = $stdout    
$stdout = File.new("mytestfile.out",'w+')
puts "this goes in mytestfile"
$stdout = old_stdout
puts "this should be on the console"
$stdout = File.new("mytestfile1.out",'w+')
puts "this goes in mytestfile1:"
$stdout = old_stdout
puts "this should be back on the console"

You only need to use reopen if you're doing something like creating child processes (e.g. with fork) and want the child's output to go elsewhere, or if you have an extension that writes directly to standard out without using Ruby's $stdout global.

In your code, when you call reopen you are redirecting both $stdout and old_stdout, as they are both just references to the same IO object, which is why you aren't getting output back to the console when you assign old_stdout back to stdout.

like image 3
matt Avatar answered Nov 08 '22 12:11

matt