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?
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"