Force bash script to use tee without piping from the command line
I have a bash script with lots of output and I would like to change that script (not create a new one) to copy all the output to a file, just like it would happen if I were piping it through tee.
I have a file script.sh:
#!/bin/bash
init
do_something_that_outputs_stuff_to_STDOUT
launch_applications_that_output_to_STDOUT
fini
and I would like to make a copy of STDOUT to a file script.log without having to type ./script.sh | tee script.log
every time.
Thanks, Tom
Solution 1:
I couldn't get Dennis' very simple one-liner to work, so here's a far more convoluted method. I'd try his first.
As mentioned, you can use exec to redirect standard error & standard out for the entire script. Like so:exec > $LOGFILE 2>&1
This will output all stderr and stdout to $LOGFILE.
Now, since you want to have this displayed to the console as well as a logfile, you're also going to have to use a named pipe for exec to write to, and tee to read from.
(Dennis' one-liner technically does this as well, although obviously in a different way) The pipe itself is created with mkfifo $PIPEFILE
. Then do the following.
# Start tee writing to a logfile, but pulling its input from our named pipe. tee $LOGFILE < $PIPEFILE & # capture tee's process ID for the wait command. TEEPID=$! # redirect the rest of the stderr and stdout to our named pipe. exec > $PIPEFILE 2>&1 echo "Make your commands here" echo "All their standard out will get teed." echo "So will their standard error" >&2 # close the stderr and stdout file descriptors. exec 1>&- 2>&- # Wait for tee to finish since now that other end of the pipe has closed. wait $TEEPID
If you want to be thorough, you can create and destroy the named pipe file at the start and end of your script.
For the record, I gleaned most of this from a random guy's very informative blog post: (Archived version)
Solution 2:
Simply add this to the beginning of your script:
exec > >(tee -ia script.log)
That will append all output sent to stdout to the file script.log
, leaving the previous contents in place. If you want to start fresh every time the script is run just add rm script.log
right before that exec
command or remove the -a
from the tee
command.
The -i
option causes tee
to ignore interrupt signals
and may allow tee
to catch a more complete output set.
Add this line to also catch all errors (in a separate file):
exec 2> >(tee -ia scripterr.out)
The spaces between the multiple >
symbols are important.