Linux command list

tee command: print to the screen and save to a file

__ __ _ _____
\ \ / /__| |_|_ _|__ _ __ _ __ ___
\ \ /\ / / _ \ '_ \| |/ _ \ '__| '_ ` _ \
\ V V / __/ |_) | | __/ | | | | | |
\_/\_/ \___|_.__/|_|\___|_| |_| |_| |_
 
A sandbox for trying tee. Nothing here can touch your real files.
user@webterm:~/project$
 

tee writes what passes through a pipe to a file as well as the screen. Learn how it differs from `>`, when to add `-a`, and why `sudo tee` is the way to edit a root-owned file, by running the commands in a real terminal in your browser.

Updated: 2026-09-06

Syntax

command | tee [options] file...

Whatever comes in goes both to the screen and to the file.

Try it first

Keep the output of a grep while still reading it.

$ grep ERROR logs/app.log | tee errors.txt
ERROR connection refused
ERROR timeout
ERROR connection refused

You saw the result, and the same lines went into the file.

$ ls
errors.txt  logs/
$ cat errors.txt
ERROR connection refused
ERROR timeout
ERROR connection refused

With > the screen stays empty. tee is for when you want to watch and keep.

Saving from the middle of a pipe

tee passes its input straight through, so it can sit anywhere in a pipeline.

$ grep ERROR logs/app.log | tee errors.txt | wc -l
3

The answer you wanted was the count, 3, and the list that produced it is still in errors.txt. It is also a handy way to inspect the middle of a long pipeline.

Appending

-a adds to the end instead of replacing.

$ echo 'ERROR manual entry' | tee -a errors.txt
ERROR manual entry
$ cat errors.txt
ERROR connection refused
ERROR timeout
ERROR connection refused
ERROR manual entry

Writing to a file you do not own

This is where tee really earns its place. Files under /etc are not yours to write.

$ echo '10.0.0.1 db' >> /etc/hosts
zsh: permission denied: /etc/hosts

Adding sudo changes nothing, because the >> is carried out by your shell. tee moves the writing into a program that can run as root.

$ echo '10.0.0.1 db' | sudo tee -a /etc/hosts
10.0.0.1 db
$ cat /etc/hosts
127.0.0.1 localhost
10.0.0.1 db

sudo tee -a is the standard way to add a line to a system config file.

Several files at once

List the names and they all get the same content.

$ grep INFO logs/app.log | tee info.txt summary.txt
INFO  server started
INFO  retrying
$ ls
errors.txt  info.txt  logs/  summary.txt

When you actually reach for it

tee is what you type to keep a record without interrupting the flow.

SituationWhat to type
Watch the result and keep itgrep ERROR app.log | tee errors.txt
Save the middle of a pipeline... | tee mid.txt | wc -l
Add a line to a system configecho "..." | sudo tee -a /etc/hosts
Accumulate a run log... | tee -a run.log

Things that trip people up

Forgetting -a overwrites. tee run.log starts the file again every time. To accumulate, always add -a.

sudo does not apply to >. Redirection is the shell's job, so sudo only elevates the command. That is exactly why sudo tee exists.

It always prints. tee cannot be quiet. When you only want the file, use > or >>.

Practise it hands-on

webterm.appthis site

learn.webterm.appa separate site

>_WEBTERM LEARN

WebTerm Learn: from one command to actually using it

Commands stick when they show up in a real sequence of work, not one at a time. There is a course that builds them up in order.

See the course

Related commands

cat / grep / wc / sudo

Frequently asked questions

How is it different from `>`?
`>` sends the output to a file and shows you nothing. tee **shows it and saves it**, and passes it along the pipe as well.
Why is it called tee?
After a T-shaped pipe fitting. One stream in, two streams out.
How do I append instead of overwrite?
Add `-a`. Without it, the file is replaced.
I used sudo and it still says permission denied.
In `sudo echo x > /etc/hosts` the `>` is handled by your shell, with your permissions, so sudo never reaches it. Write `echo x | sudo tee -a /etc/hosts` and the program doing the writing is the one running as root.
Can it write to several files at once?
Yes. List them: `tee a.txt b.txt`.