Is there any way to capture the stdout & stderr output of the current process?

One of the more common side-effects for functions to have is writing output to stdout or stderr. This poses a problem when writing unit tests in OUnit2, both because having a whole bunch of output printed to the console while running unit tests is annoying, and because sometimes it would be nice to write a unit test ensuring a function outputs a specific message.

One way to do this is to parameterize every function with two Format.formatter arguments for out and err output. However, this is fairly heavyweight when sometimes the message is printed out way down the call stack. Is there a way to just capture stdout and stderr of the current process into a buffer?

To have something similar to capturing without having extra arguments you can always

let std = ref Format.std_formatter

let err = ref Format.std_error

and then change them when needed.

I thought there was a way to do what you asked with the unix or sys module but I can’t find it.

1 Like

From memory, this can be done using a pipe. Something like (not tested):

let saved_stdout =
  Unix.dup Unix.stdout (* save current stdout to restore it later *)

let stdout_read =
  let pipe_read, pipe_write = Unix.pipe () in
  Unix.dup2 pipe_write Unix.stdout; (* Unix.stdout now points to pipe_write *)
  Unix.close pipe_write;
  pipe_read

Now you can read from stdout_read in order to “read the stdout” of the current program. If you ever want to restore the “old” stdout you do so by

let () =
  Unix.dup2 saved_stdout Unix.stdout;
  Unix.close saved_stdout

Cheers,
Nicolas

6 Likes

This is tricky because the pipe has a limited amount of buffering, so it will lose data. To avoid this you’d need to concurrently read off the pipe. I have an example here: capture_stdout_logs

3 Likes

Yet another option is to write the standard descriptors to temporary files using dup2s and read them back in your program.

If you need a bit of conceptual help with dup2, follow the course.

Regarding @lukstafi’s code I would rather do that with a thread than a domain. Domains are not really expected to be used for short lived concurrency.

4 Likes

This kind of redirection exists in ppx_expect and alcotest. I wanted to release a lib doing only that (with js support) but i never finalized the release. See capture-output/src at main · hhugo/capture-output · GitHub

3 Likes

Here is a full example using dup2, we use it to store the output of tools we launch with execvp.

2 Likes