I want to tee my stdout to a log file from inside the script. And maybe stderr too.

This requires some tricky file descriptor manipulation, and either a named pipe or Bash's ProcessSubstitution. We're going to show the Bash syntax only.

Let's start with the simplest case: I want to tee my stdout to a logfile, as well as to the screen.

This means we want two copies of everything that gets sent to stdout -- one copy for the screen (or wherever stdout was pointing when the script started), and one for the logfile. The tee program is used for this:

# Bash
exec > >(tee mylog)    

The process substitution syntax creates a named pipe (or something analogous) and runs the tee program in the background, reading from that pipe. tee makes two copies of everything it reads -- one for the mylog file (which it opens), and one for stdout, which was inherited from the script.

Because there is a background job that has to read and process all our output before we see it, this introduces some asynchronous delay. Consider a case like this:

# Bash
exec > >(tee mylog)

echo "A" >&2
cat file
echo "B" >&2

The lines A and B that are written to stderr don't go through the tee process - they are sent directly to stderr. However, the file that we get from cat is sent through our pipe and tee before we see it. If we run this script in a terminal, without any redirections, we're likely (not guaranteed!) to see something like this:

~$ ./y
A
B
~$ hi mom

There is really no way to avoid this. We could slow down stderr in a similar fashion, hoping to get lucky, but there's no guarantee that all the lines will be delayed equally.

(More to come later!)