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.

CommandPrints
pthe whole pattern space
Pthe pattern space up to the first newline
=the current line number, on its own line, before the line
lthe pattern space with escapes visible and a trailing $
l Nthe same, wrapped at N columns (l 0 never wraps). GNU only
Fthe current input file name. GNU only
w filewrite the pattern space to file
W filewrite 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/:/'