When working with shell commands, it's crucial to understand how file descriptors work. By default:
- STDOUT (Standard Output) uses file descriptor 1
- STDERR (Standard Error) uses file descriptor 2
# Basic redirection example
command > output.txt 2>&1
The tee
command is particularly useful when you want to both see output in the terminal and save it to a file simultaneously. It reads from standard input and writes to both standard output and files.
# Simple tee usage
command | tee output.txt
To properly capture both streams with tee, you need to merge STDERR into STDOUT before piping to tee:
# Correct method
command 2>&1 | tee output.txt
The incorrect approach would be:
# This won't capture STDERR in the file
command | tee output.txt 2>&1
Here are some real-world scenarios where this technique is useful:
# Example 1: Compiling software
make 2>&1 | tee build.log
# Example 2: System administration
apt-get install package 2>&1 | tee install.log
# Example 3: Script debugging
./myscript.sh 2>&1 | tee debug_output.txt
For more control over the output streams:
# Send combined output to file AND stderr to separate file
command 2>&1 | tee combined.log 2>errors.log
# Append to existing files
command 2>&1 | tee -a existing.log
The left-to-right processing of redirections in shell explains why 2>&1
must come before the pipe:
- First, merge stderr into stdout (2>&1)
- Then pipe the combined stream to tee
Reversing the order would mean tee only gets the original stdout, not the merged streams.
If you're not seeing expected results:
- Verify file permissions on the output file
- Check if any commands are buffering their output
- Test with simple commands first (e.g.,
ls
orecho
)
When working with Unix/Linux commands, developers often need to capture both standard output (STDOUT) and standard error (STDERR) streams simultaneously. While simple redirection (command > file 2>&1
) works for direct file output, piping through tee
introduces complexity in the ordering of redirections.
Consider this common mistake:
find . | tee output.txt 2>&1
This fails because:
1. STDERR (fd 2) still points to the terminal
2. Only STDOUT gets piped to tee
3. The 2>&1
applies to tee's output, not the original command
The correct syntax merges streams before piping:
find . 2>&1 | tee output.txt
This works because:
1. 2>&1
merges STDERR into STDOUT
2. The combined stream is then piped to tee
3. Both streams appear in the file and terminal
For complex commands with multiple subcommands:
(make build 2>&1; make test 2>&1) | tee build.log
When needing timestamped logging:
find /etc 2>&1 | tee -a "$(date +%Y%m%d)_system_search.log"
To separate streams while still viewing both:
command 2>&1 > >(tee stdout.log) 2> >(tee stderr.log >&2)
For process substitution in bash:
find / 2>&1 | tee >(grep -v "Permission denied" > clean.log)
Remember that:
- Redirection order matters critically
- Pipes only handle STDOUT by default
- Some programs may buffer output differently when piped
- The merged stream loses distinction between error and normal output