Bash – How to Redirect Bash stdout+stderr to One File and stderr to Another File

bashio-redirectionshellstderrstdout

I need to redirect ALL output to one file, and in addition redirect stderr to another file. Can this be done easily?

Let's assume my command for the purposes of this example is:

php /tmp/doWork.php

I can get output to separate files by using:

php /tmp/doWork.php 1> /tmp/stdout_file 2> /tmp/stderr_file

Based on this, I attempted:

php /tmp/doWork.php &> /tmp/stdboth_file 2> /tmp/stderr_file

but this just put stdout and stderr in /tmp/stdboth_file and never wrote to /tmp/stderr_file.

Best Answer

With zsh (and zsh only) and its multios feature:

your-cmd 2> stdout+stderr.log >&2 2> stderr.log

As fd 2 is redirected twice, zsh implements an internal tee to have it sent to both files.

With bash (or any Bourne-like shell (other than zsh where you'd need to disable multios for it to work here)), you can do the teeing by hand with:

{ your-cmd 2>&1 >&3 3>&- | tee stderr.log 3>&-; } > stderr+stdout.log 3>&1

(though you lose the exit status of your-cmd. zsh has it in $pipestatus[1], bash in "${PIPESTATUS[0]}" though (provided the redirection into stderr+stdout.log didn't fail)).

To record the pid of your-cmd, you could do:

{ sh -ec 'echo "$$" > /var/run/pidfile; exec your-cmd' 2>&1 >&3 3>&- |
   tee stderr.log 3>&-; } > stderr+stdout.log 3>&1

With yash and it's process redirection feature:

your-cmd > stdout+stderr.log 2>(tee stderr.log)

(but note that yash will not wait for the termination of that tee command, so the log files may not be complete by the time you run the next command after that).

Something similar (and with the same caveat) can be done with process substitution in bash, zsh and ksh93:

{ your-cmd 2> >(tee stderr.log); } > stderr+stdout.log

To run in background and get the pid:

(exec your-cmd 2> >(tee stderr.log)) > stderr+stdout.log & pid=$!

With rc:

{your-cmd |[2=0] tee stderr.log} > stdout+stderr.log

rc's pipes allow specifying which file descriptors are connected to the pipe. With other shells, it's always fd 1 of the left command and fd 0 of the right one (hence the little dance with fd 3 above to shift file descriptors around). rc will report failure if either your-cmd or tee fails, though the exact number may be lost.

Related Question