DEV Community

dpm_bush
dpm_bush

Posted on Originally published at sshflow.com

Use tee to Save Command Output Without Losing the Terminal View

When you redirect a command’s output to a file, it usually disappears from the terminal. The Linux tee command lets you do both: save a copy and keep the output flowing to your screen or to another command.

The basic pattern

tee reads from standard input, writes what it receives to standard output, and—when you give it filenames—writes a copy to each file too.

printf 'deployment started\n' | tee deploy.log
Enter fullscreen mode Exit fullscreen mode

You’ll see the line in the terminal, and deploy.log will contain the same text. You can inspect the resulting file with tools such as cat for reading files in Linux.

The general form is:

command | tee [options] file...
Enter fullscreen mode Exit fullscreen mode

A normal pipe passes the command’s standard output (stdout) to tee. Without a filename, tee simply copies standard input to standard output. With filenames, it writes the stream to those files as well.

Choose whether to replace or append

By default, tee creates each output file or replaces its existing contents. That can be surprising when you’re trying to build up a log across several runs.

Use -a to append instead:

printf 'another deployment started\n' | tee -a deploy.log
Enter fullscreen mode Exit fullscreen mode

The choice is important: tee deploy.log overwrites the file; tee -a deploy.log adds to it. If you’re capturing output that you need to keep, check which behavior you want before running the command.

You can also send the same input to more than one file:

printf 'check passed\n' | tee latest.log audit.log
Enter fullscreen mode Exit fullscreen mode

Both files receive the input, and tee also writes it to the terminal. Add -a if you want to append to both files rather than replace their contents.

Inspect a pipeline without breaking it

A useful feature of tee is that it can sit in the middle of a pipeline. It saves an intermediate stream, then passes that stream onward unchanged:

journalctl -u nginx | tee nginx.log | tail -n 20
Enter fullscreen mode Exit fullscreen mode

Here, the journal output is saved in nginx.log and passed to tail, which prints its final 20 lines. This is handy when a later filter narrows what you see but you still want a copy of the full stream. For example, you could filter the displayed output with grep patterns in Linux while saving the unfiltered input with tee.

As with any pipeline, tee only sees what arrives on its standard input. Normally, that means stdout—not error messages written to standard error (stderr).

Include error messages when needed

To capture both stdout and stderr in a Bash pipeline, redirect stderr to stdout before the pipe:

command 2>&1 | tee command.log
Enter fullscreen mode Exit fullscreen mode

The order matters. 2>&1 sends stderr to the current stdout, and then the pipe carries the combined stream to tee. The output remains visible in the terminal and is saved to command.log.

If you want to save the combined output without displaying the copy from tee, redirect its stdout to /dev/null:

command 2>&1 | tee command.log > /dev/null
Enter fullscreen mode Exit fullscreen mode

This hides the terminal copy from tee; it doesn’t change what is written to the file.

Writing to a protected file

Shell redirection happens in your current shell, before the command after the pipe starts. That’s why this often fails when your user can’t write to a system-owned file:

printf 'enabled=true\n' > /etc/myapp/example.conf
Enter fullscreen mode Exit fullscreen mode

Adding sudo to a command does not elevate the shell that handles > either. Instead, pipe the content to sudo tee, so the tee process opens the destination with elevated privileges:

printf 'enabled=true\n' | sudo tee /etc/myapp/example.conf
Enter fullscreen mode Exit fullscreen mode

This replaces the file’s contents. To append instead, use:

printf 'enabled=true\n' | sudo tee -a /etc/myapp/example.conf
Enter fullscreen mode Exit fullscreen mode

tee echoes the input to standard output even when writing a file. To suppress that display:

printf 'enabled=true\n' | sudo tee /etc/myapp/example.conf > /dev/null
Enter fullscreen mode Exit fullscreen mode

Be particularly careful with the overwrite form: it can replace an existing configuration file. Use the actual path and content intended for your system.

A quick way to choose

command | tee output.log             # Display and overwrite
command | tee -a output.log          # Display and append
command | tee first.log second.log   # Write to multiple files
command 2>&1 | tee output.log        # Include stderr in Bash
Enter fullscreen mode Exit fullscreen mode

For most troubleshooting, start with command | tee output.log. Add -a when you need to preserve earlier runs, and add 2>&1 only when error output should be included too. Keep in mind that shell and tee options can vary across Unix-like systems; check the local manual or tee --help if you rely on options beyond append mode.

I originally published a more detailed version of this guide on the SSHFlow blog.

I'm also building SSHFlow — an SSH client where every server gets its own workspace for terminals, SFTP, code, and databases.

Top comments (0)