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