capture stderr

S

Simon Strandgaard

I want to temporarily capture stderr output.
Im on a freebsd box, so pipe should work.

What am I doing wrong?

--
Simon Strandgaard


server> ruby x.rb
Loaded suite TestCapture
Started
F
Finished in 0.023457 seconds.

1) Failure:
test_capture_stderr(TestCapture) [x.rb:31]:
<"hello world"> expected but was
<"">.

1 tests, 1 assertions, 1 failures, 0 errors
server> cat x.rb
require 'test/unit'

class TestCapture < Test::Unit::TestCase
def capture_stderr(&block)
reader, writer = IO.pipe
result = ""
thread = Thread.new do
loop do
res = select([reader], nil, nil, nil)[0]
if res.include?(reader)
unless reader.eof
result += reader.read
end
end
end
end
old_stderr = $stderr.dup
$stderr.reopen(writer)
block.call
$stderr.flush
$stderr.reopen(old_stderr)
thread.kill
writer.close
reader.close
return result
end
def test_capture_stderr
str = capture_stderr {
$stderr.puts("hello world")
}
assert_equal("hello world", str)
end
end

if $0 == __FILE__
require 'test/unit/ui/console/testrunner'
Test::Unit::UI::Console::TestRunner.run(TestCapture)
end
server>
 
S

Simon Strandgaard

I want to temporarily capture stderr output.
Im on a freebsd box, so pipe should work.

What am I doing wrong?

Forgot to do thread.join ... Now it works.

BTW: Is there a platform independent way to capture
$stderr output ?

--
Simon Strandgaard


server> ruby x.rb
Loaded suite TestCapture
Started
..
Finished in 0.004465 seconds.

1 tests, 1 assertions, 0 failures, 0 errors
server> cat x.rb
require 'test/unit'

class TestCapture < Test::Unit::TestCase
def capture_stderr(&block)
reader, writer = IO.pipe
result = ""
thread = Thread.new do
fds = [reader]
loop do
res = select(fds, nil, nil, nil)[0]
if res.include?(reader)
if reader.eof
thread.kill
else
result += reader.read
end
end
end
end
old_stderr = $stderr.dup
$stderr.reopen(writer)
block.call
$stderr.flush
writer.close
$stderr.reopen(old_stderr)
thread.join
reader.close
return result
end
def test_capture_stderr
str = capture_stderr {
$stderr.puts("hello world")
}
assert_equal("hello world\n", str)
end
end

if $0 == __FILE__
require 'test/unit/ui/console/testrunner'
Test::Unit::UI::Console::TestRunner.run(TestCapture)
end
server>
 
T

Tietew

On Tue, 10 Feb 2004 20:20:03 +0900
[capture stderr]
Simon Strandgaard said:
I want to temporarily capture stderr output.

No need to reopen. Just assign StringIO.


require 'stringio'

e = StringIO.new
$stderr = e
warn "hello"
$stderr = STDERR

p e.string # => "hello\n"


--[ Tietew ]-------------------------------------------------------
Mail: (e-mail address removed) / (e-mail address removed)
Web : http://www.tietew.net/ (Tietew Windows Lab.)
PGP fingerprint: 26CB 71BB B595 09C4 0153 81C4 773C 963A D51B 8CAA
 
S

Simon Strandgaard

On Tue, 10 Feb 2004 20:20:03 +0900
[capture stderr]
Simon Strandgaard said:
I want to temporarily capture stderr output.

No need to reopen. Just assign StringIO.


require 'stringio'

e = StringIO.new
$stderr = e
warn "hello"
$stderr = STDERR

p e.string # => "hello\n"

Nice.. this indeed does the same job. Thanks.
 
A

Ara.T.Howard

Date: Tue, 10 Feb 2004 12:14:39 +0100
From: Simon Strandgaard <[email protected]>
Newsgroups: comp.lang.ruby
Subject: capture stderr

I want to temporarily capture stderr output. Im on a freebsd box, so pipe
should work.

a pipe should work - but it's pretty tricky...
What am I doing wrong?

* IO#read blocks until eof is read - your loop will only happen once and tries
to read all data at once. if you really wanted to do something like that
you'd need to do something like

require 'io/nonblock' # i love this module

buf = ''
loop do
select [r], nil, nil
buf << r.nonblock{ r.read }
end

your 'read' blocks:

~/eg/ruby > cat a.rb
r, w = IO.pipe
t = Thread.new do
puts 'selecting...'
select [r], nil, nil
p r.read
end
w.puts 42
t.join
puts 'never reached'

~/eg/ruby > ruby a.rb
selecting...
(hangs forever)


in the end, i think you can accomplish this much more easily and portably:

~/eg/ruby > cat x.rb
require 'test/unit'
require 'stringio'

class TestCapture < Test::Unit::TestCase
def capture_stderr
begin
$stderr = StringIO.new
yield
$stderr.rewind && $stderr.read
ensure
$stderr = STDERR
end
end
def test_capture_stderr
str = capture_stderr { $stderr.puts("hello world") }
assert_equal("hello world\n", str)
$stderr.puts 'and $stderr still works...'
end
end

~/eg/ruby > ruby x.rb
Loaded suite x
Started
and $stderr still works...
 
S

Simon Strandgaard

a pipe should work - but it's pretty tricky...

Yes I got the pipe working.. didn't you see my second mail? ;-)

* IO#read blocks until eof is read - your loop will only happen once and tries
to read all data at once. if you really wanted to do something like that
you'd need to do something like

require 'io/nonblock' # i love this module

I have not yet tried using this module module.. though I have experience
with non-blocking io.


[snip]
begin
$stderr = StringIO.new
yield
$stderr.rewind && $stderr.read
ensure
$stderr = STDERR
end

Thanks.. I had forgotten the 'ensure' statement!

My code is now looking like:

def capture_stderr(&block)
e = StringIO.new
$stderr = e
block.call
return e.string
ensure
$stderr = STDERR
end
 

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

No members online now.

Forum statistics

Threads
473,764
Messages
2,569,565
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top