CrackOps
Interview PrepTracksLinuxAWS LabSoonInterview RoomSoon
Sign inStart free
CrackOps

Interview prep for the people who keep production up.

[email protected]
ProductInterview PrepTracksDashboardInterview Room
RolesDevOpsSREPlatformCloud
CompanyAboutContactPrivacyTerms
© CrackOps
Linux / Text Processing / tee

tee

Copy a stream to the screen and to files at once

Beginnerpipelinesloggingsudo

tee reads standard input and writes it both to standard output and to one or more files, like a T-shaped pipe fitting. You use it to watch a long command while keeping a log, to save the middle of a pipeline, and in the sudo tee idiom for writing to root-owned files.

Syntaxtee [-a] [FILE]...

Watch it run

You run a deploy by hand and want to see progress live while keeping a copy to attach to the change ticket.

devops@devops-lab: ~/app — zsh — 96×28
➜ ~/app
Step 1 of 3
./deploy.sh 2.14.2 2>&1 | tee deploy.log

Every line appears on screen as it's produced and is also written to deploy.log. 2>&1 makes sure messages printed on stderr go through tee as well.

Anatomy of a command

$
command./deploy.sh 2.14.2

Any command whose output you want to both watch and keep.

How it works

tee is a tiny loop: it opens every file named on its command line (with O_TRUNC, or O_APPEND for -a), then repeatedly read()s a chunk from stdin and write()s the same bytes to stdout and to each file, until stdin hits end-of-file. Because tee opens the files itself, the permission check happens in tee's process. That's why sudo tee works where sudo echo ... > file fails: in the second case your own shell opens the file for the redirection before sudo even runs. tee only sees what arrives on its stdin, so stderr of the previous command bypasses it unless you add 2>&1. In a pipeline the shell's exit status is the last command's, which is tee's, and tee almost always succeeds. So a failing command piped into tee looks successful unless you use set -o pipefail or check PIPESTATUS.

Options you'll actually use

-aAppend to the files instead of overwriting them
FILE...Write a full copy to every file listed
> /dev/nullSilence tee's stdout copy when you only want the file (sudo tee case)
-iIgnore SIGINT, so Ctrl-C stops the producer but tee finishes writing
-pKeep writing to the other outputs if one pipe reader exits (GNU)
>(cmd)Process substitution: tee into another command, e.g. tee >(gzip > out.gz)

Common mistakes

sudo echo 'line' >> /etc/some.conf

The redirection is performed by your unprivileged shell before sudo runs, so it fails with Permission denied.

echo 'line' | sudo tee -a /etc/some.conf > /dev/null
echo 'nameserver 10.0.0.2' | sudo tee /etc/resolv.conf (meant to add a line)

Without -a, tee truncates the file: every existing line is gone.

echo 'nameserver 10.0.0.2' | sudo tee -a /etc/resolv.conf
make build | tee build.log in CI

The pipeline's exit status is tee's, so a failed build passes, and stderr isn't in build.log either.

set -o pipefail; make build 2>&1 | tee build.log
Using tee to log a command that prints a lot with no newlines (progress bars)

Programs often detect a pipe and change behaviour: no colours, block buffering, or carriage-return progress bars written into the log.

Use the tool's --no-progress / --quiet flags, or script -q -c 'cmd' log to keep a real terminal

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Run ls -l /etc and both see the output and save it to ~/etc-listing.txt.

  2. 2

    Append the line 'checked by devops' to ~/notes.txt using tee, without printing it on the screen.

  3. 3

    Add the line '127.0.0.2 lab-test' to /etc/hosts using sudo.

  4. 4

    Run 'ls /nope /etc' so that BOTH the error and the listing end up in ~/both.log while you watch.

Related commands

bash: redirectionRedirection: stdout, stderr, 2>&1, /dev/null and here-docsbash: exit codesExit codes, $?, set -euo pipefail and trapsudoRun a command as root or another userxargsBuild and run commands from standard input
PreviouspasteNextxargs
Linux / Text Processing / tee

tee

Copy a stream to the screen and to files at once

Beginnerpipelinesloggingsudo

tee reads standard input and writes it both to standard output and to one or more files, like a T-shaped pipe fitting. You use it to watch a long command while keeping a log, to save the middle of a pipeline, and in the sudo tee idiom for writing to root-owned files.

Syntaxtee [-a] [FILE]...

Watch it run

You run a deploy by hand and want to see progress live while keeping a copy to attach to the change ticket.

devops@devops-lab: ~/app — zsh — 96×28
➜ ~/app
Step 1 of 3
./deploy.sh 2.14.2 2>&1 | tee deploy.log

Every line appears on screen as it's produced and is also written to deploy.log. 2>&1 makes sure messages printed on stderr go through tee as well.

Anatomy of a command

$
command./deploy.sh 2.14.2

Any command whose output you want to both watch and keep.

How it works

tee is a tiny loop: it opens every file named on its command line (with O_TRUNC, or O_APPEND for -a), then repeatedly read()s a chunk from stdin and write()s the same bytes to stdout and to each file, until stdin hits end-of-file. Because tee opens the files itself, the permission check happens in tee's process. That's why sudo tee works where sudo echo ... > file fails: in the second case your own shell opens the file for the redirection before sudo even runs. tee only sees what arrives on its stdin, so stderr of the previous command bypasses it unless you add 2>&1. In a pipeline the shell's exit status is the last command's, which is tee's, and tee almost always succeeds. So a failing command piped into tee looks successful unless you use set -o pipefail or check PIPESTATUS.

Options you'll actually use

-aAppend to the files instead of overwriting them
FILE...Write a full copy to every file listed
> /dev/nullSilence tee's stdout copy when you only want the file (sudo tee case)
-iIgnore SIGINT, so Ctrl-C stops the producer but tee finishes writing
-pKeep writing to the other outputs if one pipe reader exits (GNU)
>(cmd)Process substitution: tee into another command, e.g. tee >(gzip > out.gz)

Common mistakes

sudo echo 'line' >> /etc/some.conf

The redirection is performed by your unprivileged shell before sudo runs, so it fails with Permission denied.

echo 'line' | sudo tee -a /etc/some.conf > /dev/null
echo 'nameserver 10.0.0.2' | sudo tee /etc/resolv.conf (meant to add a line)

Without -a, tee truncates the file: every existing line is gone.

echo 'nameserver 10.0.0.2' | sudo tee -a /etc/resolv.conf
make build | tee build.log in CI

The pipeline's exit status is tee's, so a failed build passes, and stderr isn't in build.log either.

set -o pipefail; make build 2>&1 | tee build.log
Using tee to log a command that prints a lot with no newlines (progress bars)

Programs often detect a pipe and change behaviour: no colours, block buffering, or carriage-return progress bars written into the log.

Use the tool's --no-progress / --quiet flags, or script -q -c 'cmd' log to keep a real terminal

Interview questions

Practice

Try these on a Linux machine of your own. Each task has a hint and a solution.

  1. 1

    Run ls -l /etc and both see the output and save it to ~/etc-listing.txt.

  2. 2

    Append the line 'checked by devops' to ~/notes.txt using tee, without printing it on the screen.

  3. 3

    Add the line '127.0.0.2 lab-test' to /etc/hosts using sudo.

  4. 4

    Run 'ls /nope /etc' so that BOTH the error and the listing end up in ~/both.log while you watch.

Related commands

bash: redirectionRedirection: stdout, stderr, 2>&1, /dev/null and here-docsbash: exit codesExit codes, $?, set -euo pipefail and trapsudoRun a command as root or another userxargsBuild and run commands from standard input
PreviouspasteNextxargs