Trying to be a little fancy but can't figure out how to do it right. Basically I want to redirect STDOUT and STDERR so that they each continue to write to their default streams but also write to a log file. The goal is that ALL of the rest of the code can be completely oblivious and continue to write to STDOUT and STDERR as normal and the Tee-ing will work automatically. I tried the following simple experiment: class Tee < IO def initialize (*fhs) @fhs = fhs end def write (string) @fhs.each do |fh| fh.print string end end end $stdout = Tee.new($stdout) puts "Hello world" system("pwd") I get the following: Hello world bar.rb:21:in `system': uninitialized stream (IOError) from bar.rb:21:in `<main>' So the native Ruby I/O seems to work but somehow the system command does not like this. I suspect what's going on is that the native Ruby I/O commands have logic to handle a true stream and a virtual stream like this differently, whereas the system command does not. Yes, I know that I could rewrite the system call to capture the output explicitly and use print to write it out but, again, the point of this experiment is to capture any and I/O and be agnostic of how the code is written. I could use pipes and such but I'm sure there must be a more elegant way to do this. Any help is appreciated. Thanks.
on 2016-09-08 20:21
on 2016-09-13 11:44
Just a guess: With your approach, you do not really handle with "stdout" (as seen by the operating system). You only modify the meaning of the variable $stdout. This goes well, while you are in the realm of Ruby, i.e. of those methods, which use $stdout for writing. A method such as system must deal with the concept of stdout as being imposed by the operating system. The writers of system()certainly did not anticipate that someone will change the variable $stdout. It is not surprising that at least something will likely go wrong. I would expect problems in a different place too: The standard output in Rub is represented by $stdout and STDOUT. You redefined the former. Unless STDOUT is represented somehow as a reference to whatever $stdout points to - and I don't think this is the case - any output going to STDOUT would not be tee'd. Try it.