The tee command is one of the most useful basic Linux commands that every user should know. In this comprehensive guide, we will start from the basics of tee and explore various useful examples, techniques, and edge cases for using it effectively. Whether you are a Linux beginner or expert, this deep dive into tee will give you new insights into this invaluable tool.
What Exactly is the Tee Command?
The tee command allows you to redirect the standard output from any other command or program to a file and also print it out to the terminal at the same time. It essentially splits the output into two streams, like the shape of capital letter ‘T‘.
The name tee comes from this T-splitter behavior: it tees the stdout into two directions.
The Syntax of tee
The basic syntax for tee is straightforward:
tee [OPTIONS] [FILE]
This takes the standard output from the previous command and:
- Writes it to the specified file
- Prints it to the terminal
The command before tee and the file to write to are mandatory. By default, tee will overwrite the output file if it already exists.
Some useful command line options are:
-aor--append: Append output to the file instead of overwriting it-ior--ignore-interrupts: Ignore interrupt signals like CTRL+C while writing the file--help: Print help text for the tee command-vor--version: Print installed version of tee
Exit Status:
The tee command returns an exit status of 0 if successful, or 1 if an error occurred like insufficient permissions or write failure. This is useful for error handling in scripts.
How Tee Splits Standard Output
To understand tee, you need to first understand I/O streams and redirection in Linux.
By default, the standard output (stdout) of a program prints to the terminal. The stdout can be redirected to a file instead, using the > redirect operator:
ls -l > files.txt
This redirects the ls output completely to files.txt.
The stdout can also be piped to another command using the | operator:
ls -l | grep .txt
Here, the output of ls is passed as input to grep.
The tee command combines both by splitting stdout into two streams:
- stdout continues printing to the terminal
- stdout also gets copied into the file
This lets you view or pipe the output, while simultaneously logging it to a file!
Practical Examples of Using tee
Now that you understand what tee does under the hood, let‘s go through some common examples of using it.
1. Save Command Output to a File
The most basic usage of tee is to save the output of a command to a file:
$ ls -l | tee filelist.txt
This will list all files and folders in the current directory, print it to the terminal as usual, and also write the output to filelist.txt.
You can view the file contents as well:
$ cat filelist.txt
2. Append to a Log File
By default, tee will overwrite any existing files. To append instead, use the -a option:
$ uptime | tee -a system.log
This will append the uptime output to the end of system.log. You can keep running this to continuously log system uptime to that file.
3. Write Output to Multiple Files
You can provide multiple file names to tee to split stdout into more than 2 destinations:
$ hostname | tee file1.txt file2.txt
This will print the hostname to terminal, and also write it into both file1.txt and file2.txt.
4. Read and Write to the Same File
A useful trick is using tee to read the contents of a file, process it, and write the modified output back to the same file.
For example, this inserts a header line into log.txt:
$ tee -a log.txt < log.txt
This reads log.txt, appends the contents back into it along with the stdout from tee, which is the header line.
This approach can be used as a mutual exclusion lock in scripts to ensure concurrent writes don‘t overwrite each other.
5. Ignore Interrupts
The -i option makes tee ignore interrupt signals like CTRL+C:
$ ping 8.8.8.8 | tee -i pinglog.txt
Now ping will run until completion even if CTRL+C is pressed, and the output is fully captured in pinglog.txt.
6. Hide Output to Terminal
To write output only to a file and not print to the terminal:
$ find /home | tee filelist.txt > /dev/null
The find output gets logged to filelist.txt. But by redirecting tee‘s stdout to /dev/null, it doesn‘t print to the terminal.
7. Pipe tee Output to Another Command
You can pipe the output from tee itself to another command:
$ ls | tee directory_list.txt | wc -l
This counts the number of items ls returns, while also logging the full output to a file.
8. Use tee in Scripts
Here is an example script that uses tee to log its output:
#!/bin/bash
printf "Script started at $(date)\n" | tee scriptlog.txt
# Rest of script execution
printf "Script finished at $(date)\n" | tee -a scriptlog.txt
This logs when the script starts and ends to scriptlog.txt while also printing it.
Why Use tee Over Alternatives?
You may wonder why use the tee command instead of simply redirecting to a file or using echo to write output? Here are some key advantages of using tee:
- Allows you to view or pipe output to another command in addition to writing it to a file. Redirecting or echo won‘t print to the terminal.
-
teeappends by default whereas>redirects always overwrite the file. -
tee -aprovides mutex locking allowing atomic read-write to the same file. -
Works consistently across Linux distros unlike
echowhich may have different flags. -
Supports ignoring signal interrupts with
-iunlike redirection. - Tee can process large multi-gigabyte outputs without loading entirely into memory.
So in summary, tee gives you more flexibility and capability than basic redirection or echo.
Advanced tee Usage Examples
Now let‘s go through some more advanced examples for using tee.
Log Database Queries
tee is handy for debugging database queries and profiling their performance:
$ psql -c "SELECT * FROM users;" | tee query.log
This logs the full SQL query result to query.log while also displaying it in the terminal.
Capture Debug Output
Tools like strace produce long and detailed debug trace. tee lets you save this debug dump to a file:
$ strace -f -e trace=network curl google.com | tee curl-trace.txt
Stream Processing
For long running outputs, tee can be used to stream the data to a file as well as another process without having to load the full output in memory.
For example, streaming a log file through grep and writing it to another log:
$ tail -f web-server.log | tee weblogs.txt | grep ERROR
This outputs only "ERROR" lines to the terminal, tee copies all logs to weblogs.txt, without having to buffer web-server.log entirely in memory.
Atomic Write-Read
As mentioned earlier, tee can read and write to the same file atomically:
$ tee file.txt < file.txt
This safely inserts data from stdin to file.txt by first reading the contents, appending the new data, then overwriting the original file. This prevents data loss if multiple processes are writing to the same file.
Potential Issues and Risks
While tee is very useful, there are some edge cases and risks to be aware of:
-
File permissions –
teewill fail if the output file can‘t be written to due to permission issues. Make sure the folder and file are writable. -
No space left –
teewill stop with an error if the disk is full and output file can‘t be written to. -
TOCTTOU attacks – Since
teereads and writes files, there is risk of time-of-check vs time-of-use (TOCTTOU) attacks especially from unprivileged scripts. Use with caution. -
Log injection –
teedoesn‘t escape log content, so writing untrusted data into logs could lead to injection attacks. -
Compatibility – There are some subtle differences in
teebehavior across Unix-like systems. Test for portability. -
Atomicity – While
tee file < fileis atomic against interrupts, two simultaneous processes can still corrupt data. Use locking where required.
Tee Implementations
The tee command is implemented in the coreutils package and follows the Single UNIX Specification behavior. Some alternate implementations include:
- busybox tee – Provides a simplified version. Doesn‘t support input from stdin.
- solarish tee – Solaris‘s System V style tee, slightly different flags.
-
MirBSD tee – Adds extra options like
-pto preserve file permissions. - GNU tee – Closely aligns with coreutils behavior but has parallel write support.
Check your system‘s man pages for implementation specific behavior of tee.
Conclusion
The humble tee command is an invaluable tool for any Linux user, with diverse practical use cases. It forms a core part of the Linux philosophy of building complex data processing pipelines using simple composable commands.
I hope this guide gave you a deep understanding of tee, how it works, various examples, and best practices. The key takeaways are:
-
Use
teeto write command output to log files while viewing or piping it. -
Append to existing files with
-ainstead of overwriting. - Write to multiple files or read and write the same file atomically.
-
Ignore interrupts with
-ifor long running commands. - Understand risks like log injection, file permissions, race conditions based on use.
Tee is easy to use but also powerful. Keep it handy in your shell toolbox as you work on Linux!



