Split redirecting to multiple files in bash A function mycommand which runs command and:


Gives me three log-files which are:


*.stdout.log: everything from stdout
*.stderr.log: everything from stderr
*.full.log: everything (i.e., both stderr and stdout)

Prints all output to screen


The idea is that I can quickly see through the errors, skim for additional info in stdout. But in some cases both (stdout and stderr) messages should be seen as one as it gives context, thus I want a third file to do that. For real-time overview I obviously want to see it on my screen too.

This works but it is rather clumsy and doesn't look nice. I'm unsure whether this is the right way to do it or I should improve the code.

The snippet is from my *.*shrc.

mycommand () { command "$@" \
          &gt; &gt;(tee command_"$@"_$(date +%F_%T).stdout.log command_"$@"_$(date +%F_%T).full.log) \
          2&gt; &gt;(tee command_"$@"_$(date +%F).stderr.log command_"$@"_$(date +%F_%T).full.log &gt;&amp;2 ) } \
There are a number of things here which concern me.

First up, the stderr log file does not have the time on the file name (missing _%T). This is a classic copy-paste+partial-fix issue, you copied the same code to multiple places, then needed to fix it, but you only fixed some of them.

The solution to that is to extract the code to just one place, and reuse that:

local basename=command_"$@"_$(date +%F_T)
tee ${basename}.stderr.log


Now, basename is reusable, and if you want to change all three (stderr, stdout, full), then you can change them in just one place.

Your code does not handle complex commands gracefully. What if the command is ls -laR /etc .... how will it save away to log files called:

command_ls -laR /etc_2015-09-22_10:25:30.stdout.log


You need to rationalize that. I started with the spaces and slashes first... using a bash regex/replace for [ \/] to be replaced with an _ underscore.

local command="$@"
local base=command_${command//[ \/]/_}_$(date +%F_%T)


Next up, I am concerned that you have two tee processes both writing to the same "full" file. The one that starts second will overwrite what the first one started with, but I suspect that they will subsequently "merge" the results. It would be better to start a clean file, and then have both tee processes append to the existing file.

I messed around with your function, and came up with:

threepipe () {
    local command="$@"
    local base=rcommand_${command//[ \/]/_}_$(date +%F_%T)
    local both=$base.both.log
    rm -f $both
    touch $both
    $command 2&gt; &gt;(tee -a $both | tee "$base.stderr.log" &gt;&amp;2) | tee -a $both | tee "$base.stdout.log"
}


Note that only the stderr needs to be handled by the anonymous fifo. The redirect back to stderr keeps it out of the way of the rest of the stdout. Also, note the use of the -a flag for tee.