Use exec to redirect all BASH script output to a file, syslog, or other command

One of the neat tricks of exec is that you can use it to redirect all output from a shell. If you take advantage of this inside of a script you can easily send all your output to a file, or even syslog.

#!/bin/bash

# redirect STDOUT to a file (note the single > -- this will truncate /tmp/outfile)
exec 1> /tmp/outfile

echo Now all STDOUT from each subsiquent command will
echo be redirected to /tmp/outfile
echo but not STDERR >&2

# redirect STDERR to STDOUT and append (note the double >>) both to /tmp/outfile
exec 1>> /tmp/outfile 2>&1

echo now we get STDERR too >&2

# We can also turn off all output
echo 1>&-
echo 2>&-

echo Now you can\'t hear me!

Now if you want to redirect all the output to another command you have to be a bit craftier. You can't simply use an unnamed pipe after exec to redirect output to another command's STDIN -- all this will accomplish is sending all of exec's output to the second command, and exec doesn't produce any output.

In this example we use a named pipe. We set our our second command's STDIN to the named pipe. In this case logger which will send all the output to syslog -- and then direct all output to the named pipe using exec.

#!/bin/bash

# location of named pipe
named_pipe=/tmp/$$.tmp

# remove pipe on the exit signal
trap "rm -f $named_pipe" EXIT

# create named pipe
mknod $named_pipe p

# start logger process in background with STDIN coming from named pipe
# also tell logger to append the script name to the syslog messages
# so we know where they came from
logger <$named_pipe -t $0 &

# or maybe you wanted a log file and output to STDOUT
# tee <$named_pipe /tmp/outfile &

# redirect stderr and stdout to named_pipe
exec 1>$named_pipe 2>&1

echo STDOUT captured
echo STDERR captured >&2

You can also use this trick to make your script's a little smarter about where they send their output. Perhaps you want the output to go to STDOUT if run from a terminal, but sent to a file when run by cron.

#!/bin/bash

# test if fd 1 (STDOUT) is NOT associated with a terminal
if [ ! -t 1 ] ; then
 # redirect STDOUT to a file (note the >> -- this will append to /tmp/outfile)
 exec 1>> /tmp/outfile
fi

echo Hello world!

If you want to switch back and forth you can also save STDIN, STDOUT and STDERR to a higher file descriptor and then restore them to their original file descriptor when you're done:

#!/bin/bash

# save stdout to fd 3 and redirect output to outfile
exec 3>&1 >> /tmp/outfile

echo output to file

# and now restore stdout from fd 3 -- and close fd 3
exec 1>&3 >&3-

echo back to stdout

(
  # or redirect within a subshell and only this output is redirected
  exec >> /tmp/outfile

  echo more output to file
)

# and we're back
echo out of the subshell output returns to stdout

# note how it effects only the subshell 
# -- if you ran these exec command from your shell:
# $ exec > /tmp/save_commandline
# your shell would redirect stdout to a file, kind of confusing but if you
# source a script that uses exec and a named pipe directed to tee you could
# make a version of the script command