7

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.

1 Answer 1

9

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.

5
  • Ok, thanks. I'd also like to send the pid of my command to a file and used to append the command with & echo $! >> /path/to/pidfile. I can still do this but will the pid be correct, or that of tee etc? Commented May 10, 2017 at 14:41
  • @LukeCousins, yes in cmd1 | cmd2 &, $! is the pid of cmd2. You can use the process substitution approach in that case. Or you can use sh -c 'echo "$$" > pidfile; exec your-cmd' in place of your-cmd. Commented May 10, 2017 at 14:47
  • Ok, in this particular use case the exit status is not important. Could you give an example of using bash with the pid file getting the right id using process substitution as I'm not quite sure. Thanks for all the help :) Commented May 10, 2017 at 14:50
  • @Luke, see edit. In the process substitution case, in the case of bash or zsh (as opposed ksh93), if you add a &, because of the command group, you'll get an extra subshell process, and $! will be the pid of that subshell. Hence the extra exec. Commented May 10, 2017 at 15:04
  • @StéphaneChazelas In your bash command { your-cmd 2>&1 >&3 3>&- | tee stderr.log 3>&-; } > stderr+stdout.log 3>&1 , what does 3>&- do and why do you need curly braces around the first 2 parts?
    – huyz
    Commented Mar 3 at 12:31

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .