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

L

Larry Fast

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?
 
J

James Edward Gray II

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 Gray II
 
N

Nick Sieger

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
 
A

ara.t.howard

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


class TeeIO
attr 'tee'

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

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


-a
 
L

Larry Fast

Nick said:
Have a look at the CI::Reporter::OutputCapture class
at line 11, and its usage on line 50:
http://tinyurl.com/29rhqj

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?
 
N

Nick Sieger

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?

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
 
L

Larry Fast

Nick said:
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
 
R

Robert Klemme

class TeeIO
attr 'tee'

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

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

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

SCNR

robert
 
E

Erik Veenstra

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"

----------------------------------------------------------------
 
L

Larry Fast

Erik said:
["$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"
 
P

Phrogz

Erik said:
["$stdout", "$stderr"].each do |std|
io = eval(std)
old_write = io.method:)write)
class << io
self
end.module_eval do
[snip]
Last question: what does "self" do on the line below "class << io"
class Rectangle; end
=> nil
=> #<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 ...
 
E

Erik Veenstra

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/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,534
Members
45,007
Latest member
obedient dusk

Latest Threads

Top