How to save stdout to file, stderr to file, stdout+stderr to file and get stdout + stderr to terminal like normal for a shell script
Simply expanding on your approach:
exec 2> >(tee -a stderr stdall) 1> >(tee -a stdout stdall)
Standard error will be written to the file named stderr
, standard output to stdout
and both standard error and standard output will also be written to the console (or whatever the two file descriptors are pointing at the time exec
is run) and to stdall
.
tee -a
(append) is required to prevent stdall
from being overwritten by the second tee
that starts writing to it.
Note that the order in which redirections are performed is relevant: the second process substitution is affected by the first redirection, i.e. the errors it emitted would be sent to >(tee -a stderr stdall)
. You can, of course, redirect the second process substitution's standard error to /dev/null
to avoid this side effect. Redirecting standard output before standard error would send every error to stdout
and stdall
too.
Since the commands in Bash's process substitutions are executed asynchronously, there is no way to guarantee that their output will be displayed in the order it was generated. Worse, fragments from standard output and standard error are likely to end up appearing on the same line.
Your script can run itself all over via $0
(with setting and checking an environment variable in order to avoid infinite recursion) instead of relying on bash's > >(...)
construct, which IMLE is capricious and unreliable.
if [ "$REDIRECTED" != 1 ]; then
export REDIRECTED=1
set -o pipefail
{ { "$0" | tee stdout >&3; } 2>&1 | tee stderr; } 3>&1 | tee stdboth
exit
fi
# rest of your script here
Since tee
does not use line buffering (nor could it be forced to do so with stdbuf(1)
), the order of the data written to stdout and stderr will not be respected in the final output. With a command which is using full buffering and writing to both stdout and stderr, even a line-buffering tee
won't help and, worse, you may get in the output lines which are half stdout and half stderr.
I don't think there's any fix for that using just the shell language and readily available command-line utilites.