Pipes and redirection: join commands together
The idea that makes the command line powerful is simple: small commands that each do one thing, joined so that the output of one becomes the input of the next. This page covers the joining.
Three streams
Every command has three channels.
| Name | Number | Normally connected to |
|---|---|---|
| standard input (stdin) | 0 | the keyboard |
| standard output (stdout) | 1 | the screen |
| standard error (stderr) | 2 | the screen |
Results go to standard output. Error messages go to standard error, so that they can be separated from results. Redirection reconnects these channels to files or to other commands.
Output into a file: >
asha@pc:~/demo$ ls > list.txt
asha@pc:~/demo$ cat list.txt
list.txt
log.txt
marks.csv
notes.txt
old
photos
report.txtNothing appeared on screen for the first command, because its output went into the file.
Danger. > empties the file before writing. If the file exists, its old contents are gone, with no question asked. In Bash and Zsh, set -o noclobber makes the shell refuse to overwrite with >.
Adding to a file: >>
asha@pc:~/demo$ echo "one more line" >> list.txt
asha@pc:~/demo$ tail -n 2 list.txt
report.txt
one more lineThe pipe
The | sign connects the output of the command on its left to the input of the command on its right.
asha@pc:~/demo$ ls | wc -l
7ls produced a list, and wc -l counted its lines. Pipes can be chained as far as you like.
asha@pc:~/demo$ cat log.txt | grep ERROR | wc -l
2
asha@pc:~/demo$ sort marks.csv | head -n 3
Asha,78
Kabir,33
Meena,92Read a pipeline from left to right, as a production line: each command receives, changes and passes on.
A real pipeline
Which kinds of message does the log contain, and how many of each?
asha@pc:~/demo$ cut -c7-11 log.txt | sort | uniq -c | sort -rn
5 INFO
2 ERROR
1 WARN Step by step: cut takes characters 7 to 11 of each line, where the level is written. sort puts equal lines together. uniq -c collapses them and counts. The final sort -rn puts the largest count first. Four small commands, no programming.
Build a pipeline one stage at a time, running it after each addition to see what the next command will receive.
Input from a file: <
< connects a file to a command's input.
asha@pc:~/demo$ wc -l < notes.txt
3It is less often needed, since most commands accept a file name directly.
Errors
Error messages travel on stream 2, so > alone does not catch them.
asha@pc:~/demo$ ls notes.txt missing.txt > out.txt
ls: cannot access 'missing.txt': No such file or directory
asha@pc:~/demo$ cat out.txt
notes.txtThe result for notes.txt went to the file. The error for the missing file still came to the screen.
| Write | To send |
|---|---|
2> errors.txt | errors to a file |
2>/dev/null | errors nowhere: discard them |
> all.txt 2>&1 | both results and errors to one file |
&> all.txt | the same, in Bash and Zsh |
asha@pc:~/demo$ ls notes.txt missing.txt > out.txt 2> err.txt
asha@pc:~/demo$ cat err.txt
ls: cannot access 'missing.txt': No such file or directory/dev/null is a special file that discards whatever is written to it. find / -name hosts 2>/dev/null hides the many "Permission denied" lines.
The order matters in > file 2>&1. It says "send output to the file, then send errors to wherever output now goes". Written the other way round, errors still reach the screen.
tee: see it and save it
tee copies its input both to a file and onward to the screen, like a T-shaped pipe joint.
asha@pc:~/demo$ ls | tee names.txt | wc -l
9tee -a appends. It is also the way to write to a protected file with sudo, since sudo echo text > file fails: the redirection is done by your own shell, not by sudo. echo text | sudo tee file works.
Running commands one after another
| Form | Meaning |
|---|---|
a ; b | run a, then b, whatever happens |
a && b | run b only if a succeeded |
a || b | run b only if a failed |
asha@pc:~/demo$ mkdir -p build && echo "folder ready"
folder ready
asha@pc:~/demo$ ls nofile 2>/dev/null || echo "not there"
not there&& is the safe way to chain steps. cd project && rm -r tmp deletes only if the cd worked. With a semicolon, a failed cd would leave rm running in the wrong folder.
xargs: turn input into arguments
Some commands take names as arguments, not as input. xargs bridges the gap.
asha@pc:~/demo$ ls *.txt | xargs wc -l
1 err.txt
8 list.txt
8 log.txt
9 names.txt
3 notes.txt
1 out.txt
5 report.txt
35 totalFor file names that may contain spaces, use find ... -print0 | xargs -0 ....
Useful commands in pipelines
| Command | Does |
|---|---|
sort | sort lines |
uniq | remove repeated neighbouring lines |
wc -l | count lines |
head, tail | first or last lines |
grep | keep matching lines |
cut | take columns |
tr | change or delete characters |
On Windows
The Command Prompt has the same >, >>, <, |, 2> and &&. The commands on either side are different, but the plumbing works the same way.
Examples shown with output were run on 12 November 2026 in a practice folder: Windows commands in cmd.exe on Windows 11, Linux commands with the GNU tools of Git Bash. The folder path and user name were replaced with made-up ones. Commands marked "not run here" are shown as you would type them.
This is an independent learning site. It is not connected with Microsoft, Apple or any Linux distribution. Windows, macOS and other names belong to their owners. Try commands first in a practice folder, never on files you cannot afford to lose.