The tee command acts as a splitter in a pipeline, reading from standard input and writing simultaneously to standard output and one or more files. Its core value is allowing operators to inspect a data stream in real time while preserving it for later processing or auditing.
command | tee [options] file [file2 ...]
Options Overview
-a, --append: Append data to files rather than overwriting them.-i, --ignore-interrupts: Continue running even after receiving interrupt signals likeSIGINT(Ctrl+C).-p: Operate in a mode that ignores interrupts; functionally equivalent to-i.-s, --silent: Suppress diagnostic messages such as errors or warnings about unwritable files.
When no file is specified, tee simply relays data from stdin to stdout.
Practical Workflows
Recording Terminal Activity
Executing a command and saving its output verbatim is one of the most common scenarios. The following example both displays build output and archives it:
make 2>&1 | tee build_$(date +%Y%m%d).log
The 2>&1 redirect ensures both standard error and standard output are captured.
Multicasting to Several Logs
Distributing a data stream across multiple files can be achieved without spawning extra tee processes. Use shell redirection to avoid duplication:
some_long_running_job | tee primary.log > secondary.log
This sends the stream to the terminal, primary.log, and secondary.log with a single tee invocation.
Selective Filtering with grep
Combining tee with grep lets you see all raw output while storing it, yet only display lines matching a pattern on the console:
ffmpeg -i input.mp4 output.avi 2>&1 | tee transcode.log | grep --color=auto 'frame='
The entire log ends up in transcode.log, but the terminal only highlights lines containing frame progress.
Inline Transformation with awk
Perform immediate field extraction without losing the original dataset:
ps aux | tee snapshot.txt | awk '$3 > 5.0 {print $2, $3, $11}'
A full snapshot of processes is saved to snapshot.txt, while the terminal displays only the PID, CPU%, and command name for processes consuming more than 5% CPU.
chaining sudo for Protected Files
Writing to privileged locations is possible by prepending sudo to the command that invokes tee. The pipe itself cannot elevate privileges, but tee can:
echo 'net.core.somaxconn=1024' | sudo tee /etc/sysctl.d/99-custom.conf
Apppending with Timestamps
Maintaining a chronological log with human-readable timestamps is straightforward:
some_process 2>&1 | while IFS= read -r line; do
echo "[$(date '+%F %T')] $line"
done | tee -a timeline.log
Each line is stamped before being displayed and appended to timeline.log.
Resource and Performance Notes
When handling high-volume streams, tee's implicit write operations can become a bottleneck. Disk I/O throttling may slow down the pipeline. In such cases, evaluate whether the immediate terminal view is necessary; if not, a simple output redirect (command > file) eliminates the extra work. For minimal overhead, consider using stdbuf or unbuffer to adjust output buffering but always measure the impact on throughput.