I'm familiar with using tee, and I know that I can simply log output with

script.sh | tee file.log

However, I want a seamless interface for the user. Is there a way to, within script.sh, run tee as if it was being done as above? Within this script there are hundreds of echoes and lots of other scripts being run, it would be difficult to individually append each to file.


Solution 1: exec

You may place an exec command at the beginning of the script:

$ cat script1
exec > >(tee file.log)
echo one
echo two

Here is an example of running script1:

$ bash script1
$ one


Observe that the shell prompt returned before the output from the script was complete. This is because the output went to a subprocess and, due to vagaries of multitasking, it completes on its own schedule. For this reason, I prefer the second solution shown below:

Solution 2: grouping

You can put the entire script in a shell group, {...}, and then redirect output from the group:

$ cat script2
echo one
echo two
} | tee file.log

This can be done for arbitrarily complex scripts. Just put { at the beginning and } | tee file.log at the end.

A sample run looks like:

$ bash script2

Notice that the output neatly completes before the prompt returns.

  • The tee approach works well, and I'm now using it. Is there a way (using date) to timestamp each line as well with this approach? – Brydon Gibson Jul 25 '16 at 13:36
  • @BrydonGibson Try: ... | while read -r line; do echo "$(date +%Y-%m-%d-%H:%M:%S)" "$line"; done | tee file.log – John1024 Jul 25 '16 at 16:30
  • That will timestamp stdout as well, won't it? At the moment I'm working on tee-ing to a named pipe and then using a similar method to yours. – Brydon Gibson Jul 25 '16 at 17:04
  • Update to this: Create a named pipe, Spawn a while loop (like yours) in the background, but instead read from a named pipe, THEN run the script and tee the file. The background loop waits for output to the pipe and the script runs seamlessly. – Brydon Gibson Jul 25 '16 at 18:11
  • Note, this doesn't write STDERR to the log, only to the console. To merge them together, add 2>&1, like so: exec > >(tee -a $log_path) 2>&1 – HeroCC Jun 16 '20 at 16:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.