Capture stderr


(Simon Strandgaard) #1

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

What am I doing wrong?

···


Simon Strandgaard

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

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


(Simon Strandgaard) #2

Forgot to do thread.join … Now it works.

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

···

On Tue, 10 Feb 2004 12:14:39 +0100, Simon Strandgaard wrote:

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

What am I doing wrong?


Simon Strandgaard

ruby x.rb
Loaded suite TestCapture
Started

Finished in 0.004465 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

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


(Tietew) #3

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: tietew@tietew.net / tietew@raug.net
Web : http://www.tietew.net/ (Tietew Windows Lab.)
PGP fingerprint: 26CB 71BB B595 09C4 0153 81C4 773C 963A D51B 8CAA

···

On Tue, 10 Feb 2004 20:20:03 +0900 In article pan.2004.02.10.11.14.38.972811@adslhome.dk [capture stderr] Simon Strandgaard neoneye@adslhome.dk wrote:

I want to temporarily capture stderr output.


(Ara.T.Howard) #4

Date: Tue, 10 Feb 2004 12:14:39 +0100
From: Simon Strandgaard neoneye@adslhome.dk
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…
.
Finished in 0.000745 seconds.

1 tests, 1 assertions, 0 failures, 0 errors

-a

···

On Tue, 10 Feb 2004, Simon Strandgaard wrote:

ATTN: please update your address books with address below!

===============================================================================

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
STP :: http://www.ngdc.noaa.gov/stp/
NGDC :: http://www.ngdc.noaa.gov/
NESDIS :: http://www.nesdis.noaa.gov/
NOAA :: http://www.noaa.gov/
US DOC :: http://www.commerce.gov/

The difference between art and science is that science is what we
understand well enough to explain to a computer.
Art is everything else.
– Donald Knuth, “Discover”

/bin/sh -c ‘for l in ruby perl;do $l -e “print “\x3a\x2d\x29\x0a””;done’
===============================================================================


(Simon Strandgaard) #5

Nice… this indeed does the same job. Thanks.

···

On Tue, 10 Feb 2004 20:35:59 +0900, Tietew wrote:

On Tue, 10 Feb 2004 20:20:03 +0900 > In article pan.2004.02.10.11.14.38.972811@adslhome.dk > [capture stderr] > Simon Strandgaard neoneye@adslhome.dk wrote:

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”


Simon Strandgaard


(Simon Strandgaard) #6

From: Simon Strandgaard neoneye@adslhome.dk

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…

Yes I got the pipe working… didn’t you see my second mail? :wink:

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

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
···

On Tue, 10 Feb 2004 08:17:06 -0700, Ara.T.Howard wrote:

On Tue, 10 Feb 2004, Simon Strandgaard wrote:


Simon Strandgaard