Shell Redirect Output To File, And Still Have It On Screen

Ever need to check the output of shell functions/scripts? If no fatal/error messages found, resume normal procedure. Otherwise, go to error handling.

Usually people solve this, by redirecting stdout/stderr to grep command. But What About Time-consuming Functions/Scripts? Say several minutes. During this period, end users are blinded with nothing printed. It could be confusing and scaring to them.

Linux Tee Command


Problem Explained

Let’s examine below code snippet.

  • L10, call a user defined function, which might be time-consuming and error-prone.
  • L11-L17, run logic conditionally, according to my_fun output.

People may be stucked at L10 for quite a while, and nowhere to check what’s going on. This is definitely not good.

 1: #!/bin/bash -e
 2: function my_fun() {
 3:     set -e
 4:     echo "Run function"
 5:     # Simulate time-consuming steps
 6:     # And they might fail as well
 7:     sleep 3
 8:     echo "Action done: my_fun"
 9: }
10: output=$(my_fun)
11: if echo "$output" | grep "Action done" \
12:                1>/dev/null 2>&1; then
13:    echo "Do something..."
14: else
15:    echo "Error. msg: $output"
16:    exit 1
17: fi
18: 
19: echo "Action Done"

Use Linux tee Command.

tee is a command using standard streams which reads standard input and writes it to both standard output and one or more files, effectively duplicating its input.

Unlike usual pipe redirection, tee can do two redirections. Using tee, users can see detail progress now.

...

$my_fun 2>&1 | tee -a /tmp/test.log

if grep "Action done" /tmp/test.log \
             1>/dev/null 2>&1; then
  ...
else
  ...
fi

Don’t Get Fooled By Exit Code.

What $? would be, after running below commands? $? is 0, zero! Surprising, isn’t it?

ls /DIR_WONTEXST | tee -a test.log
echo $?

This is a tricky part of shell scripting. Running a list of commands in one line, we get the exit code of the last command. If you want to fail this line whenever any command fails, run set -o pipefail. See more discussion in StackOverflow.

Put It All Together

  1. Redirect output to both terminal and log file by tee command
  2. Use set -o pipefail, to detect possible failures.

Updated version:

#!/bin/bash -e
function my_fun() {
    set -e
    echo "Run function"
    # Simulate time-consuming steps
    # And they might fail as well
    sleep 3
    echo "Action done: my_fun"
}

set -o pipefail
tmp_log_file=/tmp/my_fun.log

my_fun 2>&1 | tee -a "$tmp_log_file"
if grep "Action done" "$tmp_log_file" \
               1>/dev/null 2>&1; then
   echo "Do something..."
else
   echo "Error. msg: $output"
   exit 1
fi

echo "Action Done"

More Reading: Challenge Your Shell Scripts By ShellCheck


Original URL: https://dennyzhang.com/shell_tee

Connect with Denny In Slack Or MailList

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.