I've been trying to redirect the output of commands invoked by
"system", by changing the $stdout variable in Ruby.
I want to avoid using shell redirections in my commands:
system "echo 1 2 3 > log-file" # I want to avoid this
So instead I wrote code like this:
#----------------------------------------
system "echo In the beginning, to STDOUT I hope."
$saved_stdout = $stdout
$stdout = File.open("log-file", "w")
system "echo After redirect, to log-file I hope."
$stdout = $saved_stdout
system "echo Original restored, to STDOUT I hope."
#----------------------------------------
When I execute this code I get:
$ ruby test-system.rb
In the beginning, to STDOUT I hope.
$
$ cat log-file
After redirect, to log-file I hope.
Original restored, to STDOUT I hope.
$
So obviously, the "system" calls are affected by my change of $stdout
but only in *one* direction. I can't figure out why I get the current
behaviour. Why is the last system output also written to the
log file ?
I'm reasonably new to Ruby, but have used Perl for many years, so maybe
my expectations are influenced by how this can be done in Perl:
#----------------------------------------
system "echo In the beginning, to STDOUT I hope.";
open(SAVED_STDOUT, ">&STDOUT") || die;
open(STDOUT, "> log-file") || die;
system "echo After redirect, to log-file I hope.";
open(STDOUT, ">&SAVED_STDOUT") || die;
system "echo Original restored, to STDOUT I hope.";
#----------------------------------------
What is the best way to accomplish the same as above in Ruby ?
And what is the explanation of the current Ruby-behaviour ?
Thanks,
/Johan Holmberg
Try changing:
$saved_stdout = $stdout
to:
$saved_stdout = $stdout.clone
Paul
This appears to work:
$saved_stdout = $stdout.dup
$stdout = File.open("log-file", "w")
system "echo After redirect, to log-file I hope."
$stdout = $saved_stdout.dup
system "echo Original restored, to STDOUT I hope."
I'm not sure why that second "dup" is necessary, perhaps a guru can
elaborate or give a better solution.
regards,
-joe
I think the solution needs to be more complicated. Try closing the logfile
before restoring stdout:
$saved_stdout = $stdout.dup
$stdout = File.open("log-file", "w")
system "echo After redirect, to log-file I hope."
$stdout.close ###### New line
$stdout = $saved_stdout.dup ### this is line 5
system "echo Original restored, to STDOUT I hope."
The result of the system is this:
test4.rb:5: closed stream (IOError)
Note that the line number is the second assignment to stdout, not the
attempt to write to it.
It seems you have to stash the stream in a temp, reassign stdout, and then
close stdout:
$saved_stdout = $stdout.dup
$stdout = File.open("log-file", "w")
puts "echo After redirect, to log-file I hope."
$saved_internal_stdout = $stdout ###### New line
$stdout = $saved_stdout.dup
$saved_internal_stdout.close ###### New line
puts "echo Original restored, to STDOUT I hope."
By the way, it doesn't seem to me that the second dup is needed. Seems to
work without it. Why do you think it's needed?
This is all complicated enough that I'm going to extend my Fluid variables
package to handle it:
Fluid.redirect(["$stdout", File.open("logfile", 'w')]) {
puts "this goes to the logfile"
}
puts "this goes back to the old $stdout"
But I'd like to know if the above sequence of steps really works, or just
doesn't obviously fail.
--
Brian Marick, mar...@testing.com
www.testing.com - Software testing services and resources
www.testingcraft.com - Where software testers exchange techniques
www.visibleworkings.com - Adequate understanding of system internals