Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

A Few Examples of Using the Tee Command


Tee lets you read from stdin and write to both stdout and a file at the same time. It optionally supports appending to a file.

Quick Jump: A Few Examples | Real World Use Cases | Demo Video

Prefer video? There’s a video version of this blog post on YouTube that goes into a bit more detail than what’s written below.

This may help visualize what’s happening:

stdin --> tee --> stdout
  • stdin: A command’s output is sent as input to tee
  • tee: Takes that output and sends it to both stdout and a file
  • stdout: Write tee’s output to stdout (the command that produced stdin to tee)
  • file: Write tee’s output to a file

A Few Examples

Here’s a few basic examples of using tee to start things off.

Write to both stdout and a new file:
$ echo "hello world" | tee message.txt
hello world

# The above command produced this file.
$ cat message.txt
hello world
Write to both stdout and append to a file:
$ echo "hello world" | tee -a message.txt
hello world

# The above command appended to the existing file due to the `-a` flag.
$ cat message.text
hello world
hello world

This will create a new file if it doesn’t exist just like echo "hello world" >> message.txt.

Write to both stdout and multiple files:
$ echo "hello world" | tee a b c
hello world

# The above command created 3 new files.
$ cat a b c
hello world
hello world
hello world
Write to both stdout and to a file owned by root with sudo:

Once in a while you may want to write to a file owned by root when using sudo.

This won’t work:

# This doesn't work because the redirect doesn't happen as the root user.
$ sudo echo "hmm" > /etc/example.conf
zsh: permission denied: /etc/example.conf

This will work:

echo "yay" | sudo tee /etc/example.conf

# Let's check out the file (yep it exists and it's owned by root):
$ stat /etc/example.conf
  File: /etc/example.conf
  Size: 4               Blocks: 8          IO Block: 4096   regular file
Device: 820h/2080d      Inode: 345077      Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2022-12-24 10:33:38.816916746 -0500
Modify: 2022-12-24 10:33:38.816916746 -0500
Change: 2022-12-24 10:33:38.816916746 -0500
 Birth: 2022-12-24 10:33:38.816916746 -0500

This is handy if you’re using sed or other tools to perform a find / replace in a config file.

The same as above except don’t write to stdout:

Maybe you don’t want to print the output to stdout but still want the file written.

You can do that by running: echo "yay" | sudo tee /etc/example.conf > /dev/null

Real World Use Cases

I’ve used the sudo tee combo a lot in the past but another use case came up recently where I wanted to write the results of a shell script to stdout and also copy the output to my clipboard at the same time.

The condensed version was I ended up with echo "${A_REALLY_LONG_QUERY}" | tee pbcopy, this let me see the query’s output in the terminal and set up my clipboard so I could paste it into another GUI tool.

pbcopy / pbpaste are macOS command line tools for interacting with the clipboard. By the way, I didn’t switch to using a Mac. This is for a client where I’m using a company issued laptop.

The same can be achieved on Linux with xclip or a comparable tool by running echo "sweet" | tee >(xclip) if your shell supports process substitution such as bash or zsh.

There’s a couple of ways that could have been solved but tee felt like the perfect tool because echoing the query AND copying it to my clipboard was my primary goal.

In this script’s case I didn’t care about portability. It takes what’s in my clipboard as input, processes the data in a shell script and then updates my clipboard so I can quickly paste it into a GUI tool. I want to run ./myscript and be done with it.

This is why I love the command line and programming in general. The shell script and an Excel VLOOKUP() converted a 30 minute daily workflow into about a 3 minute task while removing lots of chances where human error could have crept into the process.

Using tee didn’t save a lot of time on its own but it was a piece of the puzzle to provide an elegant solution.

The script itself took 10 minutes to write. This was one of the few cases where a time investment up front to semi-automate something ended up being very much worth it!

Demo Video


  • 0:06 – How tee works
  • 0:24 – Echoing something to stdout and a file
  • 1:03 – Appending to a file
  • 1:42 – Writing to multiple files at once
  • 2:17 – Using sudo tee to redirect output to a file owned by root
  • 4:08 – Writing to stdout and the clipboard

What was the last problem you solved with tee? Let me know below.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per month (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.