How can I capture STDOUT to a string?

27,291

Solution 1

Redirect Standard Output to a StringIO Object

You can certainly redirect standard output to a variable. For example:

# Set up standard output as a StringIO object.
foo = StringIO.new
$stdout = foo
# Send some text to $stdout.
puts 'hi'
puts 'bye'
# Access the data written to standard output.
$stdout.string
# => "hi\nbye\n"
# Send your captured output to the original output stream.
STDOUT.puts $stdout.string

In practice, this is probably not a great idea, but at least now you know it's possible.

Solution 2

A handy function for capturing stdout into a string...

The following method is a handy general purpose tool to capture stdout and return it as a string. (I use this frequently in unit tests where I want to verify something printed to stdout.) Note especially the use of the ensure clause to restore $stdout (and avoid astonishment):

def with_captured_stdout
  original_stdout = $stdout  # capture previous value of $stdout
  $stdout = StringIO.new     # assign a string buffer to $stdout
  yield                      # perform the body of the user code
  $stdout.string             # return the contents of the string buffer
ensure
  $stdout = original_stdout  # restore $stdout to its previous value
end

So, for example:

>> str = with_captured_stdout { puts "hi"; puts "bye"}
=> "hi\nbye\n"
>> print str
hi
bye
=> nil

Solution 3

If activesupport is available in your project you may do the following:

output = capture(:stdout) do
  run_arbitrary_code
end

More info about Kernel.capture can be found here

Solution 4

You can do this by making a call to your R script inside backticks, like this:

result = `./run-your-script`
puts result  # will contain STDOUT from run-your-script

For more information on running subprocesses in Ruby, check out this Stack Overflow question.

Solution 5

Minitest versions:


assert_output if you need to ensure if some output is generated:

assert_output "Registrars processed: 1\n" do
  puts 'Registrars processed: 1'
end

assert_output


or use capture_io if you really need to capture it:

out, err = capture_io do
  puts "Some info"
  warn "You did a bad thing"
end
assert_match %r%info%, out
assert_match %r%bad%, err

capture_io

Minitest itself is available in any Ruby version starting from 1.9.3

Share:
27,291

Related videos on Youtube

script_kiddie
Author by

script_kiddie

scaling is the mantra of life

Updated on March 15, 2020

Comments

  • script_kiddie
    script_kiddie almost 3 years
    puts "hi"
    puts "bye"
    

    I want to store the STDOUT of the code so far (in this case hi \nbye into a variable say 'result' and print it )

    puts result
    

    The reason I am doing this is I have integrate an R code into my Ruby code, output of which is given to the STDOUT as the R code runs , but the ouput cannot be accessed inside the code to do some evaluations. Sorry if this is confusing. So the "puts result" line should give me hi and bye.

    • SwiftMango
      SwiftMango almost 10 years
      If you just want to get the stdout from external program, use result=%x{command}. Otherwise you can redirect stdio like showed by @codegnome
    • knut
      knut almost 10 years
      Are you using rinruby? I also tried to catch rinruby (R) output, but up to now without success.
    • Ciro Santilli OurBigBook.com
      Ciro Santilli OurBigBook.com about 8 years
    • fearless_fool
      fearless_fool about 8 years
      It's a nit, but stackoverflow.com/questions/4459330/… refers to stderr, this one to stdout. You could merge them, but you'd want a search for "capture stdout" to find an answer as well as "capture stderr".
  • script_kiddie
    script_kiddie almost 10 years
    This is exactly what i need .. but it does not work . can you just have a look at it again please ? its gives a blank output ..even the puts and hi abd byt insdei dont get printed . I have included require stringio .. THanks for the help ..
  • Todd A. Jacobs
    Todd A. Jacobs almost 10 years
    No, they won't get printed, because you've redirected standard output to your StringIO. Use STDOUT.puts $stdout.string to print to the original output stream instead.
  • script_kiddie
    script_kiddie almost 10 years
    .. this works but the other soln was what i was looking at .. thanks for the reply .. appreciate.
  • Jakub Jirutka
    Jakub Jirutka almost 8 years
    ActiveSupport’s Kernel#capture is deprecated and will be removed in some future release, because it’s not thread safe. See code and discussion.
  • prashant
    prashant about 7 years
    Note that this won't necessarily work with, e.g. Logger, if it captures a reference to the original $stdout before you have a chance to reassign it.
  • Brian Underwood
    Brian Underwood about 7 years
    Exactly what I needed, thanks! FYI on a cool Ruby trick, if your begin/end match the duration of your method, you can just skip them and just put the ensure at the end of your method (generally the ensure/rescue is indented at the same level as the method's def and end)
  • fearless_fool
    fearless_fool about 7 years
    @BrianUnderwood: True, but just because you can do something in Ruby doesn't always mean you should :). I included the begin/end statements mostly to make it easier for newcomers to understand. Old pros like you will know they can elide the begin/end in this case.
  • Brian Underwood
    Brian Underwood about 7 years
    I agree that there are some things you shouldn't do, but if this is a better syntax I would use it, even for beginners, because I don't think it's likely to confuse (and I love giving people an "I didn't know you could do that" moment even if that wasn't the point of the code). They say the best way to teach kids complicated words is to not shy away from them
  • Jeff
    Jeff about 5 years
    Does this work with external commands? e.g. should this work? with_captured_std_out { %x(wget http://google.com) } I cant get it to work in this context.
  • fearless_fool
    fearless_fool about 5 years
    If %x is like backticks, it returns a string -- it doesn't print to stdout -- so you neither need nor want to wrap it with_captured_std_out.
  • Artur INTECH
    Artur INTECH over 4 years
    Brilliant! One remark: I would use original_stdout, instead of old_stdout
  • Joshua Pinter
    Joshua Pinter almost 3 years
    Honestly, this was the only thing that worked for my cases where I wanted to call Rake::Task[ "mytask" ].invoke without outputting the result to STDOUT and instead capture it for later use. I restore STDOUT immediately after using an ensure to avoid any issue. This might be dangerous if misused but it works very well when used sparingly.
  • Dorian
    Dorian about 1 year
    this is a remote code execution vulnerability