Getting Started with tee - Splitting Command Output

Getting Started with tee - Splitting Command Output

What is tee?

Lina: I learned about pipes (|) to chain commands, but I want to save the intermediate results too. Is there a way to do that?
Linny-senpai: That's exactly what tee is for! Think of it like a T-shaped pipe fitting — it splits the output in two directions at once.

When you use a pipe, output flows from one command to the next and disappears. With tee, you can save the output to a file while still passing it downstream.

Normal pipe:
CommandA → CommandB  (what's in the middle is gone)

With tee:
CommandA → tee ──→ saved to file
               ↓
           flows to CommandB too

Where the name comes from

The capital letter "T" — one input, two outputs. Same idea as a T-junction in plumbing.

Basic Usage

Lina: How do I use it?
Linny-senpai: Just insert tee filename in the middle of your pipeline!
command | tee filename

Example: display ls -la output on screen and save it to list.txt

ls -la | tee list.txt

Both happen at the same time — the output appears on screen and gets written to list.txt.

What tee gives you

  • Screen output: uninterrupted, as if tee wasn't there
  • File save: happens simultaneously

tee vs > Redirect

Lina: How is command | tee file different from command > file?
Linny-senpai: Great question! With >, the output goes only to the file — nothing shows on screen. With tee, you get both.
Method Screen output File save
command yes no
command > file no yes
command | tee file yes yes

Use > when you just want to store output silently. Use tee when you want to watch it happen and save it at the same time.

Saving Logs While Watching Output

Lina: When would I actually use this?
Linny-senpai: Classic case: running a long build or install and wanting to both watch the progress and keep a log of what happened.
make install 2>&1 | tee install.log

2>&1 redirects stderr (error messages) into the same stream as stdout, so both end up in the log file.

What 2>&1 means

  • 1 = stdout (normal output)
  • 2 = stderr (error messages)
  • 2>&1 = merge stderr into stdout

Without this, error messages go only to the terminal and never reach tee.

The -a Flag: Append Instead of Overwrite

Lina: Does tee overwrite the file every time?
Linny-senpai: Yes, by default. Use -a if you want to append instead.
command | tee -a filename

Example: accumulate run timestamps in one log file

date | tee -a run.log
date | tee -a run.log
cat run.log
Mon Jun  1 03:00:00 UTC 2026
Mon Jun  1 03:00:05 UTC 2026

Without -a, every tee call overwrites the file from scratch. Use -a whenever you want to preserve previous entries.

Writing to Multiple Files

Lina: Can I save to two files at the same time?
Linny-senpai: Yep — just list them one after another.
command | tee file1 file2

Example: save to both /tmp/result.txt and ~/backup.txt

ls -la | tee /tmp/result.txt ~/backup.txt

All listed files receive the same content. You can list as many as you need.

The sudo tee Pattern

Lina: What if I need to write to a file that needs root permissions?
Linny-senpai: This is where sudo tee shines. It's a classic Linux pattern that trips up a lot of beginners.

You might expect this to work — but it doesn't:

# This fails
sudo echo "127.0.0.1 example.local" >> /etc/hosts

Why sudo echo > file fails

sudo only applies to the echo command itself. The >> redirect is processed by the shell, which runs as your regular user. The shell tries to open /etc/hosts for writing before sudo echo even runs — and gets denied.

The correct approach is sudo tee:

echo "127.0.0.1 example.local" | sudo tee -a /etc/hosts

Here, echo runs as your user (fine — it just writes to stdout), and sudo tee runs as root and does the privileged write.

tee prints the content to the terminal as well. To suppress that output, redirect stdout to /dev/null:

echo "127.0.0.1 example.local" | sudo tee -a /etc/hosts > /dev/null

Always back up before editing system files

sudo cp /etc/hosts /etc/hosts.bak

Mistakes in /etc/hosts or similar files can break your system's network resolution.

Debugging Pipelines

Lina: When I have a long pipeline, it's hard to know what's flowing through the middle.
Linny-senpai: Insert tee at any point to snapshot the data at that stage — without breaking the flow.
cat access.log | grep "ERROR" | tee /tmp/errors.txt | sort | uniq -c

What this does:

  1. grep "ERROR" filters the log
  2. tee saves those matching lines to /tmp/errors.txt
  3. sort | uniq -c counts occurrences

You can then inspect /tmp/errors.txt separately to see exactly what went into the sorting step — useful when the final output looks unexpected.

For deep pipeline debugging, add multiple tee calls at different stages:

cat data.txt | step1 | tee /tmp/stage1.txt | step2 | tee /tmp/stage2.txt | step3

Compare the stage files to isolate exactly where things go wrong.

Common Patterns at a Glance

Lina: tee is really handy! I didn't expect saving and displaying to be so easy.
Linny-senpai: Once you know sudo tee, you'll use it all the time for config file edits. It's one of those small commands that shows up everywhere in real Linux work.
Use case Command
Display and save at the same time command | tee file.txt
Append without overwriting command | tee -a file.txt
Save to multiple files command | tee file1 file2
Write to a root-owned file echo "..." | sudo tee -a /etc/hosts
Snapshot pipeline midpoint cmd1 | tee /tmp/debug.txt | cmd2
Capture all output including error command 2>&1 | tee output.log

Next Reading