Linux tee Command
last modified October 23, 2026
The tee command in Linux reads from standard input and writes it
both to standard output and to one or more files. It is named after the
T-splitter used in plumbing: the data flows through the pipeline while a copy
is saved on the side.
tee is commonly used for logging the output of commands while
watching it on the terminal, saving intermediate results of a pipeline, and
writing to files that require root privileges.
Basic usage
In the simplest case, tee copies its input to a file and to the
terminal.
echo "Hello, World!" | tee output.txt
The message is printed on the terminal and written to
output.txt. If the file exists, it is overwritten.
$ echo "Hello, World!" | tee output.txt Hello, World! $ cat output.txt Hello, World!
Appending to a file
The -a option appends to the given files instead of overwriting
them.
echo "Second line" | tee -a output.txt
The new line is added to the end of output.txt, which now
contains two lines.
$ echo "Second line" | tee -a output.txt Second line $ cat output.txt Hello, World! Second line
Writing to multiple files
tee accepts any number of file names.
date -d "2026-10-23 10:00" "+%F %T" | tee log1.txt log2.txt
The formatted date is written to both log1.txt and
log2.txt and also printed on the terminal.
$ date -d "2026-10-23 10:00" "+%F %T" | tee log1.txt log2.txt 2026-10-23 10:00:00 $ cat log1.txt log2.txt 2026-10-23 10:00:00 2026-10-23 10:00:00
Saving an intermediate result
Placed in the middle of a pipeline, tee saves the data at that
point while passing it on to the next command.
sort words.txt | tee sorted.txt | wc -l
The sorted words are stored in sorted.txt, and
wc -l receives the same data and counts the lines.
$ sort words.txt | tee sorted.txt | wc -l 5 $ cat sorted.txt bear falcon forest rock sky
Suppressing the terminal output
When only the file copy is needed, standard output can be discarded.
echo "quiet" | tee quiet.txt > /dev/null
Redirecting to /dev/null hides the output on the terminal. The
data is still written to quiet.txt.
$ echo "quiet" | tee quiet.txt > /dev/null $ cat quiet.txt quiet
Logging standard error
A pipe passes only standard output. To capture error messages and warnings, standard error must be redirected into the pipe as well.
#!/bin/bash echo "compiling sources" echo "warning: unused variable 'x'" >&2 echo "linking binary" echo "build finished"
The script prints a warning to standard error. In the first run, the warning
appears on the terminal but is missing from build.log. The
2>&1 redirection merges standard error into standard
output, so the second log is complete. Bash also offers the shorthand
|& for 2>&1 |.
$ ./build.sh | tee build.log compiling sources warning: unused variable 'x' linking binary build finished $ cat build.log compiling sources linking binary build finished $ ./build.sh 2>&1 | tee build.log compiling sources warning: unused variable 'x' linking binary build finished $ cat build.log compiling sources warning: unused variable 'x' linking binary build finished
Writing to protected files with sudo
Output redirection is performed by the shell that runs as the current user,
so sudo does not help with > or
>>. Piping to sudo tee solves this.
echo "127.0.0.1 dev.local" | sudo tee -a /etc/hosts
In the first command, sudo elevates only echo; the
redirection to /etc/hosts fails. In the second one,
tee itself runs as root and appends the line to the file. Add
> /dev/null to avoid echoing the line back.
$ sudo echo "127.0.0.1 dev.local" >> /etc/hosts bash: /etc/hosts: Permission denied $ echo "127.0.0.1 dev.local" | sudo tee -a /etc/hosts 127.0.0.1 dev.local $ tail -n 1 /etc/hosts 127.0.0.1 dev.local
Splitting a stream with process substitution
Combined with Bash process substitution, tee can feed the same
data to several commands at once.
seq 1000 | tee >(gzip > numbers.gz) >(sha256sum > numbers.sha256) | tail -n 1
The >(...) constructs look like files to tee, but
they are pipes to other commands. The numbers are compressed and checksummed
in one pass, while tail prints the last line. Decompressing the
archive yields the same checksum.
$ seq 1000 | tee >(gzip > numbers.gz) >(sha256sum > numbers.sha256) | tail -n 1 1000 $ zcat numbers.gz | sha256sum 67d4ff71d43921d5739f387da09746f405e425b07d727e4c69d029461d1f051f - $ cat numbers.sha256 67d4ff71d43921d5739f387da09746f405e425b07d727e4c69d029461d1f051f -
Exit status in pipelines
The exit status of a pipeline is that of its last command, which hides
failures of the commands before tee.
false | tee result.txt; echo "status: $?" set -o pipefail false | tee result.txt; echo "status: $?"
Although false fails, the pipeline returns 0 because
tee succeeded. With the pipefail option, the
pipeline returns the status of the last failing command. Use it in scripts
that log through tee; the PIPESTATUS array holds
the status of each command as well.
$ false | tee result.txt; echo "status: $?" status: 0 $ set -o pipefail $ false | tee result.txt; echo "status: $?" status: 1
Best practices for tee
- Log everything: Use
2>&1 | tee fileto keep both output and errors. - Append deliberately: Use
-afor log files that should grow across runs. - Use sudo tee: Pipe to
sudo teeinstead of redirecting the output ofsudo. - Keep failures visible: Enable
set -o pipefailin scripts. - Ignore interrupts when needed: The
-ioption makesteeignoreSIGINT, so it keeps writing data from a producer that handles Ctrl+C itself.
Source
In this article, we have used the tee command to copy data to
files and the terminal, append to logs, capture errors, write protected
files, and split a stream between several commands.
Author
List all Linux tutorials.