6

The documentation for redirect_stdouton version 1.1.0, that I am currently using, does not seem to give an example of how to use that function. Maybe I missed it?

I want to capture the output of println and get it back as a string.

Here is an example:

julia> VERSION
v"1.1.0"

julia> (rd, wr) = redirect_stdout();

julia> println("This is a test.")

julia> # Get back the string "This is a test."

julia> # s = do_something_with_rd(rd)

julia> # s == "This is a test."

julia> # true

Any suggestions?

Edit

Based on the accepted answer below, here is a complete solution to my question:

julia> original_stdout = stdout;

julia> (rd, wr) = redirect_stdout();

julia> println("This is a test.")

julia> s = readline(rd)
"This is a test."

julia> s == "This is a test."
true

julia> redirect_stdout(original_stdout);

julia> println("Test of orig. stdout.")
Test of orig. stdout.

Edit 2: A More Complete Example

Here is an example of testing a variety of print and println function outputs using redirection of stdout. Thanks to @Bogumił Kamiński for his answer and edit that made this more clear to me:

using Test

# Test redirect_stdout.
@testset "Example tests using redirect_stdout" begin
    original_stdout = stdout;
    (read_pipe, write_pipe) = redirect_stdout();
    print("Using print function.")
    println("Using println function.")
    println("Second use of println function.")
    println("Line 1.\nLine 2.\nLine 3.\nEND")
    println("""
    This is new line 1.
    This is new line 2. Next a Char = """)
    print('A')
    redirect_stdout(original_stdout);
    close(write_pipe)
    @test readline(read_pipe) == "Using print function.Using println function."
    @test readline(read_pipe) == "Second use of println function."
    @test read(read_pipe, String) == "Line 1.\nLine 2.\nLine 3.\nEND\n" * 
    "This is new line 1.\nThis is new line 2. Next a Char = \nA"
end

# Suppress unnecessary output when this file.
return nothing

Here is the output:

julia> include("test_redirect_stdout.jl")
Test Summary:                       | Pass  Total
Example tests using redirect_stdout |    3      3
Julia Learner
  • 2,754
  • 15
  • 35

2 Answers2

10

Just run readline on rd (or any other reading operation).

You have just to be careful that read operations on rd are blocking, i.e. the terminal will seem to hang when the operation cannot be completed. One solution is to use @async for this. For instance:

julia> (rd, wr) = redirect_stdout();

julia> @async global x = readline(rd) # if we did not put @async here the terminal would be blocked
Task (runnable) @0x0000000004e46e10

julia> x # x is yet undefined as readline is waiting for an input
ERROR: UndefVarError: x not defined

julia> println("something") # we feed data to stdout

julia> x # and readline has finished its work and bound the value to variable x
"something"

Of course if you know exactly that the data you want to read in is there just run readline or some other function and all will work without @async.

EDIT

Given the comments from SalchiPapa I think it is also add this pattern of possible usage as it is simplest to think of IMO:

original_stdout = stdout
(rd, wr) = redirect_stdout();

println("This is a test 1.")
println("This is a test 2.")
println("This is a test 3.")

redirect_stdout(original_stdout)

# you can still write to wr
println(wr, "This is a test 4.")

# you have to close it to make the read non-blocking
close(wr)

# the pipe is redirected to original stdout and wr is closed so this is non-blocking    
s = read(rd, String)
Bogumił Kamiński
  • 66,844
  • 3
  • 80
  • 107
1

You could use sprint AKA "string print":

julia> sprint(println, "This is a test")
"This is a test\n"
HarmonicaMuse
  • 7,633
  • 37
  • 52
  • 1
    Agreed, but `sprint` and `redirect_stdout` are designed to handle a different use case. I use `redirect_stdout` when I run a third-party code that writes something to `stdout` and I want to capture it. In such cases you cannot use `sprint`. [Here](https://github.com/bkamins/Workshop-on-Optimization-Techniques/blob/d722aee731310ca51187b00a82246c08fc6ef0b0/10_jl_mc/01_stock_portofolio.jl#L39) is an example capturing the output of the external solver. And the question was about how to use `redirect_stdout` (but indeed your answer is a better solution for simple cases). – Bogumił Kamiński Feb 10 '19 at 13:33
  • 1
    Maybe it could also be useful for him `Suppressor.@capture_out` https://github.com/JuliaIO/Suppressor.jl/blob/b4ff08f0fe795a2ce9e592734a758c9e6d8e2bc4/src/Suppressor.jl#L124-L152 which uses IO stream redirects underneath. – HarmonicaMuse Feb 10 '19 at 13:45
  • 1
    Good point - actually I think it is worth to update my answer given your comment. – Bogumił Kamiński Feb 10 '19 at 15:12
  • 1
    @SalchiPapa Good point about `Supprssor's @capture_out macro. I had tried it and it worked great. However, I was wanting a solution in Base Julia and the accepted answer provided that. Anyway, it is good to have another tool in the box. – Julia Learner Feb 11 '19 at 03:22
  • 1
    @Julia Learner also you could always copy paste `@capture_out` almost verbatim into your code base, as it uses only `Base`, but then you miss out updates and bug fixes. – HarmonicaMuse Feb 13 '19 at 16:45
  • 1
    @SalchiPapa Good point. I actually did that copy paste you suggest, and then decided not to use it, and then decided well maybe I would use it. I found that `@capture_out` works really well. Long term, not sure at this point whether I like `redirect_stdout` or `@caputure_out` better. I think they both have good use cases, so will probably use both. No question that `@caputure_out` is very succinct, though, which is nice. – Julia Learner Feb 14 '19 at 01:56