Somebody at work recently asked if it were possible in Bash to have stdout and stderr both visible on the terminal but also copied to separate log files.
Normally when I think of console output and file logging at the same time, I think of "tee". But it can only handle stdout.
More recently another (the same?) coworker linked to https://www.catonmat.net/blog/bash-one-liners-explained-part-three/ which among other things contains this gem:
$ command > >(stdout_cmd) 2> >(stderr_cmd)
That pipes stdout to the first command and stderr to the second.
Aha. So here's the answer to our original problem:
$ command > >(tee /tmp/stdout.log) 2> >(tee /tmp/stderr.log 1>&2)
Here's a quick test. First we'll create a command that creates both stdout and stderr:
$ function blather() {
$ echo "hi stdout"
$ echo "hi stderr" 1>&2
$ }
$ blather
hi stdout
hi stderr
$ blather > /dev/null
hi stderr
$ blather 2> /dev/null
hi stdout
Now with logs for both stdout and stderr:
$ blather > >(tee /tmp/stdout.log) 2> >(tee /tmp/stderr.log 2>&1)
hi stdout
hi stderr
$ cat /tmp/stdout.log
hi stderr
$ cat /tmp/stderr.log
hi stdout
A bit unwieldy. Maybe we can wrap this up in a function?
function logdor() {
stdout_file=$1
shift
stderr_file=$1
shift
"$@" > >(tee -a $stdout_file) 2> >(tee -a $stderr_file 1>&2)
}
(Note the use of tee -a to append rather than overwrite the log files.)
This basically works, although the console output appears after the prompt:
pw@Pauls-MacBook-Air ~ $ logdor /tmp/to1 /tmp/to2 echo hello this is stdout
pw@Pauls-MacBook-Air ~ $ hello this is stdout
pw@Pauls-MacBook-Air ~ $ cat /tmp/to1
hello this is stdout
pw@Pauls-MacBook-Air ~ $ cat /tmp/to2
pw@Pauls-MacBook-Air ~ $ logdor /tmp/to1 /tmp/to2 cat /no/such/file
cat: /no/such/file: No such file or directory
pw@Pauls-MacBook-Air ~ $ cat /tmp/to1
hello this is stdout
pw@Pauls-MacBook-Air ~ $ cat /tmp/to2
cat: /no/such/file: No such file or directory
Some other limitations: the command argument can't be a subshell like (foo;
bar). It can however be a function:
$ logdor /tmp/to1 /tmp/to2 blather
hi stdout
hi stderr
$ cat /tmp/to1
hello this is stdout
hi stdout
$ cat /tmp/to2
cat: /no/such/file: No such file or directory
hi stderr
(P.S. Yes, "logdor" is a reference to the burninator