How do I implement the Unix 'tee' function for $stdout?

I already discovered that I can redirect $stdout just by pointing it at
a new file handle. But what I really want is to have the output
continue to STDOUT but ALSO go into my logging file along with a pile of
trace data. This is very similar to the Unix program ‘tee’

Any suggestions?

On Mar 26, 2007, at 4:44 PM, Larry F. wrote:

I already discovered that I can redirect $stdout just by pointing
it at
a new file handle. But what I really want is to have the output
continue to STDOUT but ALSO go into my logging file along with a
pile of
trace data. This is very similar to the Unix program ‘tee’

Any suggestions?

Sure. I showed how to replace $stdout in a post earlier today:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/245389

Just make a TeeIO object and stick it in that variable.

Lets us know if you are still stuck and we will give more help.

James Edward G. II

On 3/26/07, Larry F. [email protected] wrote:

I already discovered that I can redirect $stdout just by pointing it at
a new file handle. But what I really want is to have the output
continue to STDOUT but ALSO go into my logging file along with a pile of
trace data. This is very similar to the Unix program ‘tee’

Any suggestions?

Funny, I was just writing this code last night for the latest release
of ci_reporter. Have a look at the CI::Reporter::OutputCapture class
at line 11, and its usage on line 50:

http://tinyurl.com/29rhqj

Cheers,
/Nick

Hi Larry,

I realize this is not what you are asking, but in the interest of
self-promotion (gag), here’s a Ruby app I wrote that functions as a
replacement for ‘tee’

http://www.awk-fu.com/software/mux/

Getting back on topic now,
JD

On Tue, 27 Mar 2007, Nick S. wrote:

at line 11, and its usage on line 50:

c a l d e r s p h e r e . n e t

Cheers,
/Nick

class TeeIO
attr ‘tee’

def initialize tee, io
@tee = tee
@io = io
end

def << buf
@tee << buf
@io << " "
@io << buf
end
end

-a

On 3/26/07, Larry F. [email protected] wrote:

The only changed section is then:
Also, in your invocation on line 50, is {|io| $stdio = io} only used for
resetting $stdout back to normal. Nothing else?

For the initial replacement as well as the resetting back to normal,
see the #initialize method where it is also used.

Seems like your mutation to use a @@class var should work fine.

/Nick

Nick S. wrote:

For the initial replacement as well as the resetting back to normal,
see the #initialize method where it is also used.
/Nick

OK, now I see it. I’m still quite green at Ruby.

… and in finish(), @captured_io.string is returning the captured text?

22 def finish
23 @assign_block.call @delegate_io
24 @captured_io.string
25 end

On 27.03.2007 03:54, [email protected] wrote:

Funny, I was just writing this code last night for the latest release
class TeeIO
@io << buf
end
end

I rarely drink tea. Can we please also have a CoffeaIO? Thank you!

SCNR

robert

Nick S. wrote:

Have a look at the CI::Reporter::OutputCapture class
at line 11, and its usage on line 50:
c a l d e r s p h e r e . n e t

Thanks Nick,
This is almost what I want. I want to MERGE $stdout, $stderr and my
debugging output. If I change @captured_io to @@captured_io and move it
outside Initialize, does everything else stay the same?

The only changed section is then:
class OutputCapture < DelegateClass(IO)
def initialize(io, &assign)
super
@delegate_io = io
@assign_block = assign
@assign_block.call self
end

  @@captured_io = StringIO.new

Also, in your invocation on line 50, is {|io| $stdio = io} only used for
resetting $stdout back to normal. Nothing else?

I already discovered that I can redirect $stdout just by
pointing it at a new file handle. But what I really want is
to have the output continue to STDOUT but ALSO go into my
logging file along with a pile of trace data. This is very
similar to the Unix program ‘tee’

Here’s the code of my own log library that does exactly what
you want.

gegroet,
Erik V. - http://www.erikveen.dds.nl/


[“$stdout”, “$stderr”].each do |std|
io = eval(std)
old_write = io.method(:write)

class << io
self
end.module_eval do
define_method(:write) do |text|
unless text =~ /^[\r\n]+$/ # Because puts calls twice.
File.open(“logfile.log”, “a”) do |f|
f.puts [std[1…-1].upcase, caller[2], text].join(" ")
end
end

   old_write.call(text)
 end

end
end

$stdout.puts “text on stdout”
$stderr.puts “text on stderr”

On 3/27/07, Larry F. [email protected] wrote:

Nick S. wrote:

For the initial replacement as well as the resetting back to normal,
see the #initialize method where it is also used.
/Nick

OK, now I see it. I’m still quite green at Ruby.

… and in finish(), @captured_io.string is returning the captured text?

Yes, see the rdocs for StringIO: http://tinyurl.com/28mod3

/Nick

On Mar 27, 10:55 am, Larry F. [email protected] wrote:

Erik V. wrote:

[“$stdout”, “$stderr”].each do |std|
io = eval(std)
old_write = io.method(:write)

class << io
self
end.module_eval do

   old_write.call(text)

[snip]

Last question: what does “self” do on the line below “class << io”

class Rectangle; end
=> nil

r = Rectangle.new
=> #Rectangle:0x2c8bfe8

class << r; self; end
=> #<Class:#Rectangle:0x2c8bfe8>

The last expression in a block is the value of that block. In the
above, ‘self’ is used to ‘return’ the singleton class that was opened
with “class << io”.

Various libraries have code like:
class Object
def singleton_class
class << self
self
end
end
end
abstracting the somewhat odd notation. With this, the original code
could be written as:

io.singleton_class.module_eval …

That’s a very elegant solution. Does IO funnel all it’s
writes through “write”? Including things like putc? And where
would I look to find this out? Is it only in the source code?

AFAIK, yes. Yes. In the source code. Probably.

Last question: what does “self” do on the line below “class
<< io”

The common way to define a method of an object is this:

class << an_object
def method
# …
end
end

The problem is scope: variables declared before “class <<
an_object” aren’t available within the “class << an_object”
block. (And variables declared before “def method” aren’t
available within the “def method” block. But that can be worked
around by using “define_method(method)”.) Defining a method
both within the context of “an_object” and within the local
scope of variables, can be achieved with this:

class << an_object
self
end.module_eval do
define_method(method) do
# …
end
end

gegroet,
Erik V. - http://www.erikveen.dds.nl/

Erik V. wrote:

["$stdout", “$stderr”].each do |std|
io = eval(std)
old_write = io.method(:write)

class << io
self
end.module_eval do

   old_write.call(text)

Thanks Eric,

That’s a very elegant solution. Does IO funnel all it’s writes through
“write”? Including things like putc? And where would I look to find this
out? Is it only in the source code?

Last question: what does “self” do on the line below “class << io”