Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to pipe stdout and stderr separately to two different processes while letting them appear in the terminal?

I have a process that generates output both on stderr and stdout.

I need to pipe these two different commands, but I would also like to keep seeing them on the terminal.

So I tried something like this as a proof of concept:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

#!/usr/bin/env bash

set -e

function generate_output() {
  echo This message goes to stderr 1>&2
  echo This message goes to stdout
}

generate_output \
    1> >(tee <&0 >(cat > out.log)) \
    2> >(tee <&0 >(cat > err.log))

cat > out.log is a dummy command that will be replaced by something else when I figure out how to make this work.

It almost works :

$ cat err.log 
This message goes to stderr

And I see the output on the terminal.

So far so good !

But :

$ cat out.log 
This message goes to stdout
This message goes to stderr

Why does the "stderr" message ends up in out.log ?

What puzzles me even more is that if I remove the tee command, the log file contain the expected result (but then I loose terminal output)

#!/usr/bin/env bash

set -e

function generate_output() {
  echo This message goes to stderr 1>&2
  echo This message goes to stdout
}

generate_output \
    1> >(cat > out.log) \
    2> >(cat > err.log)

>Solution :

Both tees are writing to stdout. That’s fine for the first one but a problem for the second one since when the 2> redirection is processed 1> has already redirected stdout. That means that the second tee isn’t writing to the terminal, but to the first tee process. Oops!

An elegant way to fix it is to have each of the tees write to the fd that they’re reading from. Adding >&2 to the second one will fix the problem. And since I can tell you like parallel structure, you can add an explicit >&1 to the first one as well.

This will have the nice effect of preserving the separation of stdout and stderr in case there are any downstream consumers of your script’s output.

generate_output \
    1> >(tee <&0 >(cat > out.log) >&1) \
    2> >(tee <&0 >(cat > err.log) >&2)

You can then eliminate some redundancies:

  • <&0 redirects stdin into stdin, which does nothing.
  • >(cat >file) is a complicated way of writing file. You can get the same effect just by passing out.log and err.log directly to tee.
generate_output \
    1> >(tee out.log >&1) \
    2> >(tee err.log >&2)
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading