How do I write standard error to a file while using "tee" with a pipe?

bash, linux, tee, unix

Solution

I'm assuming you want to still see standard error and standard output on the terminal. You could go for Josh Kelley's answer, but I find keeping a `tail` around in the background which outputs your log file very hackish and cludgy. Notice how you need to keep an extra file descriptor and do cleanup afterward by killing it and technically should be doing that in a `trap '...' EXIT`.

There is a better way to do this, and you've already discovered it: `tee`.

Only, instead of just using it for your standard output, have a tee for standard output and one for standard error. How will you accomplish this? Process substitution and file redirection:

command > >(tee -a stdout.log) 2> >(tee -a stderr.log >&2)

Let's split it up and explain:

> >(..)

`>(...)` (process substitution) creates a FIFO and lets `tee` listen on it. Then, it uses `>` (file redirection) to redirect the standard output of `command` to the FIFO that your first `tee` is listening on.

The same thing for the second:

2> >(tee -a stderr.log >&2)

We use process substitution again to make a `tee` process that reads from standard input and dumps it into `stderr.log`. `tee` outputs its input back on standard output, but since its input is our standard error, we want to redirect `tee`'s standard output to our standard error again. Then we use file redirection to redirect `command`'s standard error to the FIFO's input (`tee`'s standard input).

See Input And Output

Process substitution is one of those really lovely things you get as a bonus of choosing Bash as your shell as opposed to `sh` (POSIX or Bourne).

In `sh`, you'd have to do things manually:

out="${TMPDIR:-/tmp}/out.$$" err="${TMPDIR:-/tmp}/err.$$"
mkfifo "$out" "$err"
trap 'rm "$out" "$err"' EXIT
tee -a stdout.log < "$out" &
tee -a stderr.log < "$err" >&2 &
command >"$out" 2>"$err"

Problem

I know how to use `tee` to write the output (standard output) of `aaa.sh` to `bbb.out`, while still displaying it in the terminal: ``` ./aaa.sh | tee bbb.out ``` How would I now also write standard error to a file named `ccc.out`, while still having it displayed?

Original source