sed Cheatsheet
Print and Quiet Mode
Use this sed reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Auto-print, and turning it off
By default sed prints the pattern space at the end of every cycle. -n suppresses
that, leaving you in full control.
sed '' file # cat sed -n '' file # nothing at all sed -n 'p' file # cat again, explicitly sed 'p' file # every line TWICE, auto-print plus p
That doubling is the fastest way to tell whether you remembered -n.
| Command | Prints |
|---|---|
p | the whole pattern space |
P | the pattern space up to the first newline |
= | the current line number, on its own line, before the line |
l | the pattern space with escapes visible and a trailing $ |
l N | the same, wrapped at N columns (l 0 never wraps). GNU only |
F | the current input file name. GNU only |
w file | write the pattern space to file |
W file | write up to the first newline to file. GNU only |
Using -n as a grep
sed -n '/error/p' app.log # grep error sed -n '/error/!p' app.log # grep -v error sed -n '/error/Ip' app.log # grep -i error sed -n '5,10p' file # a slice sed -n '$p' file # tail -1 sed -n '1p' file # head -1 sed -n '/A/,/B/p' file # a block sed -n 's/.*id=\([0-9]*\).*/\1/p' app.log # extract and print in one step
The last form is the one that earns sed its keep. p fires only if the
substitution matched, so it filters and transforms at the same time. In grep plus
sed you would scan the line twice.
# pull every href out of an HTML file, one per line sed -n 's/.*href="\([^"]*\)".*/\1/p' page.html
That one only catches the first href per line, because s without g replaces
once and .* is greedy. For several per line, split them first:
sed 's/href=/\ &/g' page.html | sed -n 's/^href="\([^"]*\)".*/\1/p'
P and D, the multi-line pair
P and D only differ from p and d when the pattern space holds more than one
line, which happens after N.
# every line except the last, one cycle at a time sed -n '$!{N;P;D;}' file # print the line BEFORE each match sed -n '$!N; /\nTARGET/P; D' file # collapse runs of blank lines to one sed -e '/^$/{N;/^\n$/D' -e '}' file
- P prints the first line of the pattern space, D removes it and restarts the
script without reading new input.
- If D finds no newline, it behaves exactly like d.
- That "restart without reading" is what makes N;P;D a sliding window rather than
a batch of pairs.
l, for seeing what is really there
l is the debugging command. It shows tabs, CRs, non-printable bytes, and marks the
end of the pattern space with $.
printf 'a\tb\r\n' | sed -n l # a\tb\r$ # find Windows line endings sed -n '/\r$/l' file sed -n -e '/\r$/=' file # just the line numbers # see what N actually built printf 'a\nb\n' | gsed -n 'N;l' # a\nb$ printf 'a\nb\n' | sed -n 'N;l' # a$ # b$ <-- macOS sed does not escape the embedded newline
macOS l is the weaker of the two. It leaves backslashes, embedded newlines, and
non-ASCII bytes as they are, so a\b prints as a\b$ and hé prints as hé$.
GNU escapes all three (a\\b$, h\303\251$) and wraps at 70 columns, which l 0
turns off. l N is GNU only, macOS answers extra characters at the end of l
command. And BSD's -l flag is unrelated, it means line-buffered output.
Writing to files
sed -n '/ERROR/w errors.log' app.log # split matching lines out sed -n -e '/ERROR/w errors.log' -e '/WARN/w warns.log' app.log sed 's/a/b/w changed.log' file # only the lines s actually changed sed -n '/x/w /dev/stdout' file # same as p, on both platforms
- The w filename runs to the end of the line, so it cannot be followed by ;
or another command. It is always the last thing on its line.
- Every w target is truncated when sed starts, even if nothing ever matches.
- /dev/stdout and /dev/stderr work as w targets on macOS and Linux. GNU also
special-cases them so they work where the device files do not exist.
Numbering and counting
sed -n '$=' file # line count, like wc -l sed -n '/error/=' file # line numbers of matches, like grep -n sed = file | sed 'N;s/\n/\t/' # number every line, like nl or cat -n sed -n '/A/,/B/=' file # line numbers of a block
= always prints on its own line, which is why the second sed is needed to join
the number to its text.
# grep -n, done in one process sed -n '/error/{=;p;}' app.log | sed 'N;s/\n/:/'