This cheat sheet covers:
>
>>
2>
2>&1
|
tee
<(process substitution)
>(process substitution)These work in shells like bash and zsh.
Most shell commands have three default streams:
| Stream | Number | Meaning |
|---|---|---|
stdin |
0 |
Input to a command |
stdout |
1 |
Normal output |
stderr |
2 |
Error output |
Example:
ls file-that-exists missing-fileThis may produce:
file-that-exists
ls: cannot access 'missing-file': No such file or directory
The filename goes to stdout.
The error message goes to stderr.
> sends normal output to a file.
ls > files.txtHow it works:
lsproduces normal output.>redirects stdout intofiles.txt.- Nothing appears in the terminal.
- If
files.txtalready exists, it is overwritten.
Example:
echo "hello" > message.txtThis writes:
hello
to message.txt.
echo "new content" > notes.txtIf notes.txt already contained something, it is replaced.
Before:
old content
After:
new content
date > run.logHow it works:
dateprints the current date and time.>writes that output torun.log.
>> appends normal output to a file.
echo "another line" >> notes.txtHow it works:
echoproduces normal output.>>adds it to the end ofnotes.txt.- Existing content is preserved.
Example:
echo "first" > log.txt
echo "second" >> log.txt
echo "third" >> log.txtNow log.txt contains:
first
second
third
date >> run.logHow it works:
- Adds the current date to the end of
run.log. - Does not erase previous log entries.
Useful in scripts:
echo "Backup started" >> backup.log
tar -czf backup.tar.gz project/ >> backup.log
echo "Backup finished" >> backup.logThis records normal output from the script in backup.log.
2> redirects error output.
ls missing-file 2> error.logHow it works:
ls missing-fileproduces an error.2>redirects stderr toerror.log.- The error does not appear in the terminal.
Example output in error.log:
ls: cannot access 'missing-file': No such file or directory
ls existing-file missing-file 2> error.logHow it works:
- Normal output for
existing-filestill appears in the terminal. - Error output for
missing-filegoes toerror.log.
So you may see:
existing-file
And error.log gets:
ls: cannot access 'missing-file': No such file or directory
ls missing-file 2>> error.logHow it works:
- Appends error output to
error.log. - Does not overwrite the file.
Useful for long-running scripts:
./deploy.sh 2>> deploy-errors.logThis saves only errors from deploy.sh.
command > output.log 2> error.logHow it works:
>sends stdout tooutput.log.2>sends stderr toerror.log.
Example:
ls file1 missing-file > output.log 2> error.logIf file1 exists and missing-file does not:
output.log contains:
file1
error.log contains:
ls: cannot access 'missing-file': No such file or directory
2>&1 means:
send stderr to wherever stdout is currently going
This is commonly used to combine normal output and errors.
command > combined.log 2>&1How it works:
> combined.logsends stdout tocombined.log.2>&1sends stderr to the same place as stdout.- Both normal output and errors go into
combined.log.
Example:
ls file1 missing-file > combined.log 2>&1combined.log may contain:
file1
ls: cannot access 'missing-file': No such file or directory
This works:
command > combined.log 2>&1This is different:
command 2>&1 > combined.logWhy?
In this version:
command 2>&1 > combined.logThe shell does this from left to right:
2>&1sends stderr to wherever stdout currently goes.- At that moment, stdout is still the terminal.
> combined.logthen sends stdout to the file.- stderr still goes to the terminal.
So:
command > combined.log 2>&1means both stdout and stderr go to the file.
But:
command 2>&1 > combined.logmeans stdout goes to the file, stderr stays on the terminal.
In bash, this redirects both stdout and stderr:
command &> combined.logEquivalent to:
command > combined.log 2>&1Append both stdout and stderr:
command &>> combined.logEquivalent to:
command >> combined.log 2>&1Example:
make &> build.logHow it works:
- Saves normal compiler output.
- Saves compiler errors.
- Terminal stays clean.
/dev/null is a special file that throws away anything written to it.
command > /dev/nullHow it works:
- Normal output is discarded.
- Errors still appear.
Example:
curl https://example.com > /dev/nullThe downloaded HTML is discarded.
command 2> /dev/nullHow it works:
- Error messages are discarded.
- Normal output still appears.
Example:
find / -name notes.txt 2> /dev/nullHow it works:
find /searches the whole filesystem.- Some directories may give permission errors.
2> /dev/nullhides those errors.- Matching filenames still appear.
command > /dev/null 2>&1or:
command &> /dev/nullHow it works:
- stdout is discarded.
- stderr is sent to stdout’s destination.
- Both are thrown away.
Example:
grep -q "hello" file.txt > /dev/null 2>&1Usually grep -q already suppresses output, but this pattern is common for quiet checks.
A pipe sends stdout from one command into stdin of another command.
command1 | command2How it works:
command1produces output.|passes that output tocommand2.command2reads it as input.
Example:
ls | grep ".txt"How it works:
lslists files.grep ".txt"filters only lines containing.txt.
ls | grep ".txt" | wc -lHow it works:
lslists files.grep ".txt"keeps only lines containing.txt.wc -lcounts the remaining lines.
ps aux | grep nginxHow it works:
ps auxlists running processes.grep nginxfilters lines containingnginx.
Better version:
ps aux | grep "[n]ginx"Why?
grep nginxmay also match thegrep nginxprocess itself.grep "[n]ginx"matchesnginx, but the grep command line does not literally contain the stringnginx.
dmesg | lessHow it works:
dmesgprints kernel messages.lesslets you scroll through them.
Useful with long output:
journalctl -xe | lesscat names.txt | sort | uniqHow it works:
cat names.txtprints the file.sortsorts the lines.uniqremoves adjacent duplicate lines.
Better version:
sort names.txt | uniqEven better:
sort -u names.txtsort access.log | uniq -c | sort -nrHow it works:
sort access.loggroups identical lines together.uniq -ccounts each group.sort -nrsorts numerically in reverse order.
By default, | pipes stdout, not stderr.
command | grep "text"How it works:
- stdout from
commandgoes togrep. - stderr from
commandstill appears on the terminal.
Example:
ls existing-file missing-file | grep fileHow it works:
existing-filegoes through the pipe togrep.- The error for
missing-filebypasses the pipe and appears on the terminal.
command 2>&1 | grep "error"How it works:
2>&1sends stderr to stdout.|pipes the combined stream togrep.
Example:
make 2>&1 | grep -i "error"How it works:
makeproduces normal output and errors.2>&1combines both streams.grep -i "error"filters lines containingerror, ignoring case.
In bash and zsh:
command |& grep "error"Equivalent to:
command 2>&1 | grep "error"Example:
make |& grep -i "warning"How it works:
- Sends both stdout and stderr from
makeintogrep. - Shows only lines containing
warning.
tee writes input to both the terminal and a file.
command | tee file.txtHow it works:
commandproduces output.tee file.txtdisplays it on the terminal.- It also saves it to
file.txt.
Example:
ls | tee files.txtHow it works:
- Shows the file list in your terminal.
- Saves the same list to
files.txt.
echo "hello" | tee message.txtHow it works:
- Prints
helloto the terminal. - Writes
hellotomessage.txt. - If
message.txtexists, it is overwritten.
echo "another line" | tee -a message.txtHow it works:
- Prints
another lineto the terminal. - Appends it to
message.txt. - Existing content is preserved.
make | tee build.logHow it works:
- You see the build output live.
- A copy is saved to
build.log.
But this only captures stdout.
To capture stdout and stderr:
make 2>&1 | tee build.logor:
make |& tee build.logHow it works:
- Both normal output and errors are combined.
teeshows them in the terminal.teesaves them tobuild.log.
This often fails:
sudo echo "hello" > /etc/example.confWhy?
sudo echo "hello"runsechoas root.- But
>is handled by your current shell, not bysudo. - Your non-root shell tries to write to
/etc/example.conf. - Permission denied.
Correct version:
echo "hello" | sudo tee /etc/example.confHow it works:
echo "hello"runs normally.- The output is piped to
sudo tee. teeruns as root and writes to/etc/example.conf.
Hide the terminal copy:
echo "hello" | sudo tee /etc/example.conf > /dev/nullHow it works:
teewrites to the protected file.> /dev/nulldiscards the copy thatteewould print to the terminal.
echo "new setting" | sudo tee -a /etc/example.confHow it works:
-atellsteeto append.sudo teehas permission to write to the protected file.
< sends a file into a command’s stdin.
command < file.txtExample:
wc -l < file.txtHow it works:
file.txtis used as input towc -l.wc -lcounts lines.- Because the file is redirected rather than passed as an argument, the output is just the number.
Compare:
wc -l file.txtOutput:
12 file.txt
With redirection:
wc -l < file.txtOutput:
12
while read line; do
echo "Line: $line"
done < names.txtHow it works:
names.txtis redirected into thewhileloop.read linereads one line at a time.- The loop prints each line with a prefix.
Safer version:
while IFS= read -r line; do
echo "Line: $line"
done < names.txtWhy?
IFS=preserves leading and trailing whitespace.-rprevents backslash escapes from being interpreted.
A here string sends a short string into stdin.
command <<< "some input"Example:
grep "cat" <<< "the cat sat"How it works:
- The string
the cat satis sent intogrep. grep "cat"searches it.
Output:
the cat sat
Another example:
wc -w <<< "hello world from shell"How it works:
- Sends the string into
wc -w. wc -wcounts words.
Output:
4
A here document sends multiple lines into stdin.
command << EOF
line one
line two
EOFExample:
cat << EOF
hello
world
EOFHow it works:
- Everything between the first
EOFand the finalEOFis sent tocat. catprints it.
Output:
hello
world
cat > config.txt << EOF
host=localhost
port=8080
debug=true
EOFHow it works:
- The here document provides input to
cat. > config.txtredirectscatoutput intoconfig.txt.- The file is created with those three lines.
cat << 'EOF'
The variable is $HOME
EOFHow it works:
- Quoting
'EOF'prevents shell expansion. $HOMEstays literal.
Output:
The variable is $HOME
Without quotes:
cat << EOF
The variable is $HOME
EOFThe shell expands $HOME first.
Output might be:
The variable is /home/lee
Process substitution lets a command treat another command’s output like a file.
command <(other-command)This is very useful for commands that expect filenames instead of piped input.
diff <(ls dir1) <(ls dir2)How it works:
ls dir1produces a list of files.ls dir2produces another list.<(...)makes each output look like a temporary file.diffcompares those two temporary-looking files.
diff <(sort file1.txt) <(sort file2.txt)How it works:
sort file1.txtproduces sorted output.sort file2.txtproduces sorted output.diffcompares the sorted results.- No separate sorted files are created.
comm compares two sorted files.
comm -12 <(sort list1.txt) <(sort list2.txt)How it works:
sort list1.txtsorts the first list.sort list2.txtsorts the second list.comm -12shows only lines common to both.- Process substitution avoids temporary files.
comm -23 <(sort old.txt) <(sort new.txt)How it works:
commneeds sorted input.-23suppresses columns 2 and 3.- It shows lines only found in
old.txt.
grep -f <(printf "error\nfatal\npanic\n") app.logHow it works:
printfcreates a list of grep patterns.<(...)turns that list into a file-like input.grep -freads patterns from that file-like input.app.logis searched forerror,fatal, orpanic.
Output process substitution sends output into another command as if it were writing to a file.
command > >(other-command)This is less common than <(...), but powerful.
echo "hello world" > >(tr a-z A-Z)How it works:
echowrites output.> >(...)redirects that output intotr.tr a-z A-Zconverts lowercase to uppercase.
Output:
HELLO WORLD
command > >(tee stdout.log) 2> >(tee stderr.log >&2)How it works:
- stdout goes to
tee stdout.log. - stderr goes to
tee stderr.log. >&2sends stderr output back to the terminal as stderr.- Both streams are logged separately while still appearing on screen.
Example:
make > >(tee build-output.log) 2> >(tee build-errors.log >&2)How it works:
- Normal build output is saved in
build-output.log. - Build errors are saved in
build-errors.log. - You still see both in the terminal.
ps aux | grep nginx > nginx-processes.txtHow it works:
ps auxlists processes.grep nginxfilters nginx lines.>saves the filtered output tonginx-processes.txt.
find / -name "*.conf" 2> errors.log | grep nginxHow it works:
find / -name "*.conf"searches for config files.- Permission errors from
findgo toerrors.log. - Successful results go through the pipe.
grep nginxfilters the successful results.
find / -name "*.conf" 2>&1 | grep deniedHow it works:
findproduces normal results and permission errors.2>&1combines stderr with stdout.grep deniedfilters both streams.
make 2>&1 | tee build.log | grep -i errorHow it works:
make 2>&1combines stdout and stderr.tee build.logsaves the full build output and passes it on.grep -i errordisplays only lines containingerror.
Result:
build.logcontains everything.- Terminal displays only error lines.
command > output.txtWrites stdout to output.txt.
command >> output.txtAdds stdout to the end of output.txt.
command 2> errors.txtWrites stderr to errors.txt.
command > all.log 2>&1Writes both stdout and stderr to all.log.
command >> all.log 2>&1Appends both stdout and stderr to all.log.
command > /dev/nullUseful when you only care about failures.
command 2> /dev/nullUseful when permission errors are noisy.
command > /dev/null 2>&1Runs silently.
command | tee output.txtShows stdout and saves it.
command | tee -a output.txtShows stdout and appends it.
command 2>&1 | tee output.txtShows and saves both stdout and stderr.
command |& grep patternFilters both stdout and stderr through grep.
diff <(command1) <(command2)Compares command outputs without temporary files.
Read redirections from left to right.
command > file 2>&1Means:
- Send stdout to
file. - Send stderr to wherever stdout now goes.
- Result: both go to
file.
But:
command 2>&1 > fileMeans:
- Send stderr to where stdout currently goes: the terminal.
- Send stdout to
file. - Result: stdout goes to
file, stderr goes to terminal.
| Syntax | Meaning |
|---|---|
command > file |
Write stdout to file, overwriting |
command >> file |
Append stdout to file |
command 2> file |
Write stderr to file, overwriting |
command 2>> file |
Append stderr to file |
command > file 2>&1 |
Write stdout and stderr to same file |
command &> file |
Bash shortcut for stdout and stderr to file |
command < file |
Use file as stdin |
command1 | command2 |
Pipe stdout from command1 into command2 |
command 2>&1 | command2 |
Pipe stdout and stderr into command2 |
command |& command2 |
Bash shortcut for piping stdout and stderr |
command | tee file |
Show output and save to file |
command | tee -a file |
Show output and append to file |
<(command) |
Treat command output as a file |
>(command) |
Redirect output into a command |
/dev/null |
Throw output away |
command > output.txtSave normal output.
command >> output.txtAppend normal output.
command 2> errors.txtSave errors.
command > output.txt 2> errors.txtSave output and errors separately.
command > all.log 2>&1Save everything.
command 2>&1 | tee all.logShow everything and save everything.
command | grep patternFilter normal output.
command |& grep patternFilter normal output and errors.
diff <(sort a.txt) <(sort b.txt)Compare transformed command outputs without temporary files.