tee Output Splitter

tee cheatsheet — split stdout to terminal and file simultaneously. command | tee output.log, tee -a to append, tee f1 f2 to write multiple files. Essential for logging pipelines.

4 min read

What it is

tee is a command-line utility that reads from standard input and writes to standard output and one or more files simultaneously, allowing you to split a data stream.

Installation

Linux: tee is typically pre-installed on most Linux distributions. If not, you can install it via your package manager:

sudo apt update && sudo apt install coreutils  # Debian/Ubuntu
sudo yum install coreutils                   # CentOS/RHEL
sudo dnf install coreutils                   # Fedora

macOS: tee is pre-installed on macOS.

Windows: tee is not a built-in Windows command. You can obtain it through:

  • Git Bash: If you have Git for Windows installed, tee is available within the Git Bash terminal.
  • Cygwin: Install the coreutils package via the Cygwin setup.
  • WSL (Windows Subsystem for Linux): Install a Linux distribution (e.g., Ubuntu) from the Microsoft Store, and tee will be available within that Linux environment.

Commands / Usage

Basic Usage

Writing to a file and stdout:

echo "Hello, world!" | tee output.txt
  • echo "Hello, world!": Sends the string "Hello, world!" to standard output.
  • |: Pipes the standard output of echo to the standard input of tee.
  • tee output.txt: Reads from standard input, writes to output.txt, and also writes to standard output.

Output:

Hello, world!

And output.txt will contain:

Hello, world!

Appending to Files

Appending to a file and stdout:

echo "Another line." | tee -a log.txt
  • -a: Appends the output to the specified file(s) instead of overwriting them.

Writing to Multiple Files

Writing to stdout and two files:

ls -l | tee file_list.txt file_list.bak
  • ls -l: Lists directory contents in long format to standard output.
  • tee file_list.txt file_list.bak: Writes the output of ls -l to both file_list.txt and file_list.bak, and also prints it to standard output.

Ignoring Interrupts

Ignoring SIGINT (Ctrl+C):

yes | tee -i output.log
  • -i: Prevents tee from being killed by SIGINT (usually Ctrl+C). This is useful when you want to ensure output is written even if the upstream command is interrupted.
  • yes: Continuously outputs "y" followed by a newline to standard output.

Common Patterns

Logging command output and errors to a file:

ls /path/to/nonexistent_dir 2>&1 | tee command_log.txt
  • ls /path/to/nonexistent_dir: Attempts to list a directory that likely doesn’t exist, producing an error on stderr.
  • 2>&1: Redirects standard error (file descriptor 2) to standard output (file descriptor 1).
  • | tee command_log.txt: Writes both the standard output (which now includes errors) to command_log.txt and displays it on the terminal.

Saving output of a long-running process:

./my_long_script.sh | tee script_output.log
  • Runs my_long_script.sh, saves its output to script_output.log, and displays it on the terminal simultaneously.

Creating a backup while processing:

cat data.csv | tee data.csv.bak | grep "important" > filtered_data.csv
  • cat data.csv: Reads the content of data.csv.
  • tee data.csv.bak: Writes the content to data.csv.bak (creating a backup) and passes it through to standard output.
  • grep "important": Filters the stream for lines containing "important".
  • > filtered_data.csv: Redirects the filtered output to filtered_data.csv.

Using tee with process substitution (Bash/Zsh):

diff file1.txt file2.txt > >(tee diff_output.txt)
  • >(...): Process substitution. tee runs in a subshell, and its standard output is treated as a file that diff can write to. This is less common than standard piping but can be useful in specific scenarios. In this case, diff’s output is sent to tee which writes it to diff_output.txt and stdout.

Gotchas

  • Overwriting vs. Appending: By default, tee overwrites files. Always use -a if you intend to append.
  • Error Handling: tee itself generally won’t report errors if it can’t write to a file (e.g., due to permissions). The error will usually come from the shell attempting the redirection, or the upstream command might fail.
  • Buffering: Standard output buffering can sometimes affect how quickly you see output on the terminal versus when it appears in the file, especially with very fast-producing commands. Using stdbuf or ensuring commands flush their buffers can help.
  • tee -i and Interrupts: While -i prevents tee from being killed by SIGINT, the upstream command might still be killed if it receives the signal. If you want to ensure the entire process (upstream and tee) continues, you might need more complex signal handling or nohup.