Linux Pipes — Cheatsheet
The pipelines worth memorising, with every stage explained. Companion to Lecture 6 (Pipes & IPC).
1. How a pipeline works
The shell writes | as a pipe: it connects the stdout of the left command to the stdin of the right command. Both run at the same time, not one after the other. Neither program contains a line of pipe code — the shell arranged the plumbing with pipe(), fork(), dup2() and exec() before either started.
ls -l | sort -n -k 5 | tail -n 1 | awk '{print $NF}'
Prints the name of the largest file in the current directory.
ls -lLong listing, one file per line. Field 5 is the size in bytes.sort -n -k 5-k 5sorts on field 5,-ncompares those fields as numbers, not text (so 9 sorts before 100).tail -n 1Keeps only the last line — the biggest, since the sort was ascending.awk '{print $NF}'NFis the number of fields on the line, so$NFis the last one: the filename.
Four processes, three pipes, all alive at once. That is the whole idea: each stage does one job, and the stream flows through.
| sends output to another program. > sends it to a file (overwrite), >> appends, and < feeds a file in as stdin. cmd > file | grep x does not work — the output already left for the file.
2. Counting & looking
ls -1 | wc -l
How many entries are in this directory?
ls -1One entry per line (digit one, not letter L). Hidden dotfiles are skipped; add-Ato include them.wc -lCounts lines on stdin.
ls -l | wc -l. That one is off by one, because ls -l prints a total 48 header line that wc happily counts.grep -c "error" logfile
How many lines contain "error"? No pipe needed.
grep -cCounts matching lines directly.grep x file | wc -ldoes the same thing with an extra process.
./myprogram | head -n 20
Look at the first 20 lines of a noisy program.
./myprogramWrites to stdout, which is now the pipe.head -n 20Prints 20 lines, then exits and closes its read end.
SIGPIPE comes from. When head exits, your program's next write() has no reader; the kernel sends it SIGPIPE and it dies. That is normal and it is why yes | head -3 terminates at all.sed -n '81,100p' file.txt
Show lines 81–100.
sed -nSuppresses the default "print every line".'81,100p'For lines 81 through 100, dop(print). The pipeline version,head -n 100 | tail -n 20, also works and is easier to remember.
dmesg | less
Page through long output instead of losing it off the top of the screen.
dmesgKernel ring buffer — thousands of lines.lessInteractive pager./wordsearches,qquits, arrows scroll.
3. Text: sort, uniq, cut, awk
sort file.txt | uniq -c | sort -nr | head -n 10
The ten most common lines, with counts. The single most useful pipeline in Unix.
sortGroups identical lines next to each other. Required — see the note.uniq -cCollapses each run of identical lines into one, prefixed with the count.sort -nrSorts by that count,-nnumerically,-rdescending.head -n 10Top ten.
uniq only looks at adjacent lines. Without the first sort it silently under-counts, and you get a wrong answer with no error. If you remember one rule from this page, make it this one.cut -d: -f1 /etc/passwd | sort
Every username on the machine, alphabetically.
cut -d: -f1Splits each line on:and keeps field 1./etc/passwdis colon-separated.sortAlphabetical order.
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head
Which clients hit this server most?
awk '{print $1}'Prints field 1 of every line (the IP).awksplits on whitespace by default, so runs of spaces do not confuse it the waycut -d' 'would.sort | uniq -cThe count-the-duplicates idiom from above.sort -rn | headBiggest first, top ten (headdefaults to 10).
tr -s '[:space:]' '\n' < essay.txt | sort | uniq -c | sort -nr | head
Word frequency.
< essay.txtFeeds the file in as stdin. Nocatrequired.tr -s '[:space:]' '\n'Translates every whitespace character to a newline;-ssqueezes repeats so blank lines do not pile up. Result: one word per line.sort | uniq -c | sort -nr | headCount and rank, as before.
grep -v '^#' config.conf | grep -v '^$'
A config file with the comments and blank lines stripped out.
grep -v '^#'-vinverts the match;^#is "hash at start of line". So: drop comments.grep -v '^$'^$is a line with nothing between start and end. So: drop blank lines.
sed 's/localhost/127.0.0.1/g' hosts.txt | tee fixed.txt | wc -l
Substitute, save the result, and count it in one pass.
sed 's/old/new/g'Substitute;gmeans every occurrence on the line, not just the first.tee fixed.txtWrites stdin to the file and passes it along. A T-junction in the pipe.wc -lCounts what came through.
4. Processes & the system
ps aux | grep myprogram | grep -v grep
Is my program running, and what is its PID?
ps auxEvery process on the machine, with user, PID, %CPU, %MEM and command.grep myprogramKeeps the lines mentioning it.grep -v grepRemoves thegrepprocess itself, which is running with "myprogram" in its own command line.pgrep -a myprogramavoids the whole problem.
ps aux | sort -rn -k 3 | head -n 5
The five processes burning the most CPU.
sort -rn -k 3Field 3 ofps auxis %CPU. Numeric, reversed.head -n 5Worst five. Swap to-k 4for memory.
ls /proc/$(pgrep -n myprogram)/fd | wc -l
How many file descriptors does my program have open? The fastest way to catch a missing close().
pgrep -n myprogramThe PID of the newest matching process.$( ... )Command substitution: run it and paste its output into the command line./proc/PID/fdA directory with one entry per open descriptor. On Linux the kernel invents it on the fly; symlinks point at files, sockets orpipe:[12345].wc -lThe count. Watch it climb in a leaky loop.
ls -l /proc/$(pgrep -n myprogram)/fd | grep pipe
Which of those descriptors are pipes, and which pipe is which.
ls -lShows the symlink targets.grep pipeKeeps thepipe:[N]ones. Two descriptors showing the same N are the two ends of one pipe — exactly what you are counting in Lab 2.
history | awk '{$1=""; print}' | sort | uniq -c | sort -nr | head
Which commands do I actually type?
historyYour shell history, each line numbered.awk '{$1=""; print}'Blanks out field 1 (the number) and prints the rest.sort | uniq -c | sort -nr | headRank them.
ss -tlnp | grep LISTEN
What is listening on a port?
ssSocket statistics; the modern replacement fornetstat.-tlnptTCP,llistening only,nnumeric ports (no DNS lookups),pshow the owning process.
5. Building & debugging your C
These are the ones you will use every week in this course.
make 2>&1 | head -n 30
See the first compiler errors, not the last screenful.
2>&1Redirects stderr (fd 2) into whatever stdout (fd 1) currently is — the pipe. Compiler errors go to stderr, and stderr is not piped by default. Without this you pipe an empty stream.head -n 30The first error is the one that matters; the rest are usually fallout.
make 2>&1 | head works. make | head 2>&1 does not: by the time you redirect, stdout has already been pointed at the pipe and stderr is still going to the terminal.make 2>&1 | grep -E 'error|warning'
Just the problems, out of a wall of build output.
grep -EExtended regular expressions, so|inside the quotes means "or". The quotes are what stop the shell from reading that|as another pipe.
./myprogram | diff - expected.txt
Does my output match the expected output? No temp file.
diff - expected.txtA lone-means "read stdin here". So the left side of the diff is your program's live output. Silence means identical;echo $?gives 0.
./myprogram | tee out.txt | tail -n 5
Keep the full output for later while watching the end of it now.
tee out.txtFull stream to the file, full stream onward.tail -n 5Last five lines on screen.
strace -f -e trace=pipe,fork,clone,read,write,close ./myprogram 2>&1 | less
Watch the actual system calls. When a pipe program hangs, this shows you exactly which read() is blocked.
strace -fTrace system calls,-ffollows children throughfork()— essential, since your bug is usually in the child.-e trace=...Only these calls, otherwise the noise is unreadable.2>&1 | lessstracewrites to stderr, so redirect before paging.
valgrind --leak-check=full ./myprogram 2>&1 | tail -n 20
The leak summary without scrolling.
valgrindReports leaks and invalid memory use, on stderr.tail -n 20The summary block lives at the end.
grep -rn "close(" src/ | wc -l
Count your closes — the first thing to check when a pipeline deadlocks.
grep -rn-rrecurses into the directory,-nprints line numbers so you can jump straight there.wc -lFour closes per pipe per process pair is the target.
find . -name '*.c' -print0 | xargs -0 wc -l | sort -n
Line counts for every C file, smallest to largest.
find . -name '*.c'Recursive search by name. Quote the pattern so the shell does not expand it first.-print0/xargs -0Separate names with a NUL byte instead of a newline. NUL is the one character a filename cannot contain, so this survives spaces in names. Plain| xargsbreaks onmy file.c.xargsTurns lines of stdin into arguments for a command. Necessary becausewctakes filenames as arguments, not on stdin.
6. Files & disk
du -sh * | sort -h | tail -n 5
What is eating my disk quota?
du -sh *Disk usage,-sone summary line per entry,-hhuman-readable (4.0K,12M,1.3G).sort -hSorts those human-readable suffixes correctly —-nalone would rank9Kabove1G.tail -n 5The five biggest.
df -h | grep -v tmpfs
Real filesystems and their free space, without the noise.
df -hFree space per mounted filesystem, human-readable.grep -v tmpfsDrops the in-memory filesystems you do not care about.
find . -name '*.o' -print0 | xargs -0 rm -f
Delete every object file in the tree.
find ... -print0Find them, NUL-separated.xargs -0 rm -fHand them tormin batches.
rm -f with echo and read the list before you delete anything. A wrong find pattern piped into rm is how people lose work.tar -czf - project/ | ssh user@vm 'cat > backup.tgz'
Archive a directory straight onto another machine, no temp file.
tar -czf -Create, gzip, and write the archive to-, meaning stdout.ssh user@vm 'cat > backup.tgz'sshforwards its stdin to the remote command, which writes it to a file there. The pipe crossed a network.
7. The tools, one line each
Almost every pipeline is these pieces in a different order.
| Tool | Reads stdin? | What it does |
|---|---|---|
wc -l | yes | Counts lines. -w words, -c bytes. |
head -n N | yes | First N lines, then exits (and may SIGPIPE the writer). |
tail -n N | yes | Last N lines. -f follows a growing file. |
sort | yes | Sorts lines. -n numeric, -r reverse, -k N by field, -h human sizes, -u dedupe. |
uniq | yes | Collapses adjacent duplicates. -c count, -d only dupes. Sort first. |
grep | yes | Keeps matching lines. -v invert, -i ignore case, -n line numbers, -r recurse, -c count, -E extended regex. |
cut | yes | Picks columns. -d delimiter, -f fields. One delimiter char only. |
awk | yes | Field-aware scripting. $1 first field, $NF last, splits on whitespace runs. |
sed | yes | Stream edit. s/a/b/g substitute, -n '5,9p' print a range, /x/d delete. |
tr | yes | Translate or delete characters. -d delete, -s squeeze repeats. |
tee | yes | Writes stdin to a file and passes it on. -a appends. |
less | yes | Pager. / search, q quit. |
xargs | yes | Turns stdin lines into command arguments. Pair -0 with find -print0. |
find | no | Walks a tree. Produces names for the rest of the pipeline. |
ps, ls, du, df | no | Sources. They start pipelines, they never sit in the middle. |
Anything in the "no" rows can only be the first stage. Anything in the "yes" rows can sit anywhere.
8. Gotchas that will bite you
1. stderr does not go through the pipe
./prog | grep error shows nothing if the errors were printed to stderr. Use ./prog 2>&1 | grep error. To pipe only stderr: ./prog 2>&1 1>/dev/null | grep error.
2. uniq needs a sort first
It only collapses adjacent lines. Unsorted input gives a wrong count and no warning.
3. $? is the last command's status
false | true; echo $? prints 0, because true succeeded. To catch a failure anywhere in the chain use set -o pipefail, or inspect ${PIPESTATUS[@]} for every stage.
4. Exit code 141 is not a crash
141 = 128 + 13 = killed by SIGPIPE. It means a downstream stage (usually head) stopped reading. Expected behaviour, not a bug in your program.
You will not see it in $?, though — for yes | head -3 that reports head's 0. The 141 belongs to the writer: check ${PIPESTATUS[0]}, or run the program on its own.
5. Output through a pipe is block-buffered
A C program that is line-buffered to a terminal becomes 4 KiB block-buffered to a pipe, so output appears in bursts or not at all until exit. In C, fflush(stdout) or setvbuf. At the shell, stdbuf -oL ./prog | grep x, or grep --line-buffered.
6. Each stage runs in its own process
So echo hello | read var; echo $var prints nothing — read ran in a subshell and its variable died with it. Use read var <<< "hello" instead.
7. Useless use of cat
cat file | grep x works, but grep x file and grep x < file do the same with one less process. Harmless, but graders notice.
8. Quote your regexes
grep -E error|warning file is parsed by the shell as a pipe. Quote it: grep -E 'error|warning' file.
9. Do not parse ls in scripts
Fine interactively, as in section 1. In a script, filenames with spaces or newlines will break it — use find -print0 with xargs -0.