How do I write stderr to a file while using "tee" with a pipe? How do I write stderr to a file while using "tee" with a pipe? unix unix

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


I'm assuming you want to still see STDERR and STDOUT 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 exra FD 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 stdout, have a tee for stdout and one for stderr. 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 STDOUT of command to the FIFO that your first tee is listening on.

Same thing for the second:

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

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

See http://mywiki.wooledge.org/BashGuide/InputAndOutput

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"' EXITtee -a stdout.log < "$out" &tee -a stderr.log < "$err" >&2 &command >"$out" 2>"$err"


why not simply:

./aaa.sh 2>&1 | tee -a log

This simply redirects stderr to stdout, so tee echoes both to log and to screen. Maybe I'm missing something, because some of the other solutions seem really complicated.

Note: Since bash version 4 you may use |& as an abbreviation for 2>&1 |:

./aaa.sh |& tee -a log


This may be useful for people finding this via google. Simply uncomment the example you want to try out. Of course, feel free to rename the output files.

#!/bin/bashSTATUSFILE=x.outLOGFILE=x.log### All output to screen### Do nothing, this is the default### All Output to one file, nothing to the screen#exec > ${LOGFILE} 2>&1### All output to one file and all output to the screen#exec > >(tee ${LOGFILE}) 2>&1### All output to one file, STDOUT to the screen#exec > >(tee -a ${LOGFILE}) 2> >(tee -a ${LOGFILE} >/dev/null)### All output to one file, STDERR to the screen### Note you need both of these lines for this to work#exec 3>&1#exec > >(tee -a ${LOGFILE} >/dev/null) 2> >(tee -a ${LOGFILE} >&3)### STDOUT to STATUSFILE, stderr to LOGFILE, nothing to the screen#exec > ${STATUSFILE} 2>${LOGFILE}### STDOUT to STATUSFILE, stderr to LOGFILE and all output to the screen#exec > >(tee ${STATUSFILE}) 2> >(tee ${LOGFILE} >&2)### STDOUT to STATUSFILE and screen, STDERR to LOGFILE#exec > >(tee ${STATUSFILE}) 2>${LOGFILE}### STDOUT to STATUSFILE, STDERR to LOGFILE and screen#exec > ${STATUSFILE} 2> >(tee ${LOGFILE} >&2)echo "This is a test"ls -l sdgshgswogswghthb_this_file_will_not_exist_so_we_get_output_to_stderr_aronkjegralhfaffls -l ${0}