# tee — Display Output and Write It to Files at Once

> Practical guide to tee — read from standard input and write to standard output and one or more files at once. Ideal for logging output in pipelines.

Source: https://www.jpkc.com/db/en/cheatsheets/files-text/tee/

<!-- PROSE:intro -->
tee reads from standard input and writes the result to standard output and one or more files at the same time – like a T-junction in a pipe that branches off the data stream. That lets you watch a command's output live on screen while also logging it to a file, without breaking the pipeline. By default tee overwrites the target file; with `-a` you append instead. Its best-known trick is the `sudo tee` pattern: because a `>` redirect is performed by the shell, not by `sudo`, `… | sudo tee file` is the clean way to write to root-owned files.
<!-- PROSE:intro:end -->

## Basic Usage

`<command> | tee <file>` — Write command output to both the screen and a file (overwrites file).

```bash
ls -la | tee listing.txt
```

`<command> | tee -a <file>` — Append output to a file instead of overwriting.

```bash
echo 'new entry' | tee -a log.txt
```

`<command> | tee <file1> <file2>` — Write output to multiple files simultaneously.

```bash
date | tee log1.txt log2.txt backup.txt
```

`<command> | tee /dev/stderr` — Duplicate output to stderr (useful for logging in pipelines).

```bash
curl -s api.example.com | tee /dev/stderr | jq '.data'
```

## Pipeline Patterns

`<cmd1> | tee <file> | <cmd2>` — Save intermediate output while continuing the pipeline.

```bash
cat data.csv | tee raw-backup.csv | sort -t',' -k2 > sorted.csv
```

`<cmd1> | tee >(cmd2) | <cmd3>` — Send output to a command via process substitution AND continue the pipeline.

```bash
ls -la | tee >(grep '.log' > logs.txt) | wc -l
```

`<cmd1> | tee >(cmd2) >(cmd3) > /dev/null` — Fan out output to multiple commands without terminal display.

```bash
cat data.txt | tee >(wc -l > count.txt) >(grep ERROR > errors.txt) > /dev/null
```

`<command> 2>&1 | tee <file>` — Capture both stdout and stderr to a file and display.

```bash
make build 2>&1 | tee build.log
```

## Writing with Elevated Privileges

`echo '<text>' | sudo tee <file>` — Write to a root-owned file (sudo redirect doesn't work with > alone).

```bash
echo 'nameserver 8.8.8.8' | sudo tee /etc/resolv.conf
```

`echo '<text>' | sudo tee -a <file>` — Append to a root-owned file.

```bash
echo '192.168.1.100 myserver' | sudo tee -a /etc/hosts
```

`echo '<text>' | sudo tee <file> > /dev/null` — Write to a root-owned file without echoing to the screen.

```bash
echo 'vm.swappiness=10' | sudo tee /etc/sysctl.d/99-swap.conf > /dev/null
```

`cat <source> | sudo tee <dest> > /dev/null` — Copy a file to a root-owned location.

```bash
cat nginx.conf | sudo tee /etc/nginx/sites-available/mysite > /dev/null
```

## Logging & Debugging

`<script> 2>&1 | tee -a <logfile>` — Run a script and log all output (stdout + stderr) with append.

```bash
./deploy.sh 2>&1 | tee -a /var/log/deploy.log
```

`script -q /dev/null <command> | tee <file>` — Capture output from commands that detect non-terminal output.

```bash
script -q /dev/null ls --color | tee colored-output.txt
```

`<command> | tee >(logger -t <tag>)` — Send pipeline output to syslog via process substitution.

```bash
backup.sh 2>&1 | tee >(logger -t backup)
```

`<command> | tee >(ts '[%Y-%m-%d %H:%M:%S]' >> <logfile>)` — Add timestamps to logged output using ts (from moreutils).

```bash
tail -f /var/log/app.log | tee >(ts '[%Y-%m-%d %H:%M:%S]' >> timestamped.log)
```

## Common Use Cases

`tar czf - <dir> | tee <archive> | md5sum` — Create an archive and calculate its checksum in one pass.

```bash
tar czf - /var/www | tee backup.tar.gz | md5sum > backup.md5
```

`<command> | tee /dev/tty | <next_command>` — Display output on the terminal while piping it to the next command.

```bash
find . -name '*.log' | tee /dev/tty | xargs rm
```

`echo '<config>' | tee <file1> <file2> <file3> > /dev/null` — Write the same content to multiple config files at once.

```bash
echo 'export NODE_ENV=production' | tee .env .env.local > /dev/null
```

`diff <(command1) <(command2) | tee diff-output.txt` — Compare two command outputs and save the diff.

```bash
diff <(curl -s api.example.com/v1) <(curl -s api.example.com/v2) | tee api-diff.txt
```

`cat <<'EOF' | sudo tee <file> > /dev/null\n<content>\nEOF` — Write a multi-line heredoc to a root-owned file.

```bash
cat <<'EOF' | sudo tee /etc/nginx/conf.d/app.conf > /dev/null
server {
    listen 80;
    server_name example.com;
}
EOF
```

<!-- PROSE:outro -->
## Conclusion

tee bridges the gap between "see it on screen" and "write it to a file" – indispensable for logging build and deploy runs, writing to root-owned files (`sudo tee`), and fanning a stream out to several destinations via process substitution. Mind two things: tee overwrites without warning if you forget `-a` – for logging, `-a` is almost always the right choice. And to capture error messages too, redirect stderr onto stdout first with `2>&1`, because tee only sees the stdout stream of the pipe. In a pipeline the exit status comes from the last command by default, which is tee itself; if you need the status of the actual command, set `set -o pipefail`.

## Further Reading

- [GNU coreutils: tee](https://www.gnu.org/software/coreutils/manual/html_node/tee-invocation.html) – official reference with all options
- [tee(1) man page](https://man7.org/linux/man-pages/man1/tee.1.html) – the Linux manual page for tee
<!-- PROSE:outro:end -->

## Related Commands

- [xargs](https://www.jpkc.com/db/en/cheatsheets/files-text/xargs/) – turn input into arguments for further commands
- [tail](https://www.jpkc.com/db/en/cheatsheets/files-text/tail/) – follow log files live, often the counterpart to tee logs
- [less](https://www.jpkc.com/db/en/cheatsheets/files-text/less/) – page through large output and log files

