sed Cheatsheet
Substitution
Use this sed reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Anatomy of s///
[addr]s/regex/replacement/flags
sed 's/cat/dog/' file # first match on each line sed 's/cat/dog/g' file # every match on each line sed '3s/cat/dog/' file # only line 3 sed '/^Host/s/cat/dog/' file # only lines starting with Host sed -n 's/cat/dog/p' file # print only the lines that changed
The regex is BRE unless you pass -E. The replacement is not a regex, it is a
template with three special characters: &, \N, and \ itself.
Flags
| Flag | Effect | Portable? |
|---|---|---|
g | replace every match, not just the first | POSIX |
N | replace only the Nth match (s/a/X/3) | POSIX |
Ng | replace the Nth match and all after it | GNU only |
p | print the pattern space if a replacement happened | POSIX |
w file | append the changed line to file | POSIX |
i or I | case-insensitive match | both, not POSIX |
m or M | multiline, ^ and $ match at embedded newlines (needs a multi-line pattern space, so pair it with N) | GNU only |
e | run the resulting pattern space as a shell command | GNU only |
printf 'a a a a\n' | sed 's/a/X/3' # a a X a, both platforms printf 'a a a a\n' | gsed 's/a/X/3g' # a a X X, GNU only printf 'a a a a\n' | sed 's/a/X/3g' # BSD: "more than one number or 'g'" printf 'ABC\n' | sed 's/abc/x/I' # x, both platforms
p combined with -n is the standard "grep and rewrite in one pass":
# print just the version numbers, nothing else sed -n -E 's/^version[[:space:]]*=[[:space:]]*(.+)$/\1/p' config.ini
The replacement side
| Token | Means |
|---|---|
& | the entire match |
\1 … \9 | the Nth \(...\) or (...) capture group |
\& | a literal ampersand |
\\ | a literal backslash |
\n \t | newline, tab. Modern GNU and modern macOS sed, not old BSD |
\U \L \u \l \E | case conversion, GNU only |
| a real newline (backslash-escaped) | portable multi-line replacement |
# wrap every match in brackets printf 'cat\n' | sed 's/cat/[&]/' # [cat] # a literal ampersand printf 'cat\n' | sed 's/cat/A\&B/' # A&B # swap two capture groups printf 'john smith\n' | sed -E 's/(\w+) (\w+)/\2, \1/' # GNU \w printf 'john smith\n' | sed -E 's/([a-z]+) ([a-z]+)/\2, \1/' # portable
GNU case conversion, which has no portable equivalent:
printf 'hello world\n' | gsed -E 's/[a-z]+/\u&/g' # Hello World printf 'hello\n' | gsed 's/.*/\U&/' # HELLO printf 'HeLLo\n' | gsed 's/.*/\L&/' # hello printf 'ab cd\n' | gsed -E 's/(\w+) (\w+)/\U\1\E \2/' # AB cd
On BSD, uppercase a match with tr or awk instead:
printf 'hello world\n' | awk '{ print toupper($1), $2 }'
Alternate delimiters
Any character can follow s, which is how you avoid escaping every slash in a path.
sed 's|/usr/local|/opt|' file # readable sed 's#/usr/local#/opt#' file sed 's,/usr/local,/opt,' file sed 's@/usr/local@/opt@' file
The same works for an address, with a leading backslash to announce the delimiter:
sed -n '\%/usr/local%p' file # equivalent to /\/usr\/local/p
- The chosen delimiter must be escaped if it appears inside the regex.
- Do not pick a regex metacharacter. s.a.b. technically works but . still means
"any character" in the pattern, so it matches far more than you meant.
- s|...|...| is the right default for anything containing paths or URLs.
Newlines, in and out
The pattern space normally holds one line with no trailing newline, so \n
never appears in it and $ matches the end of the line.
# this can never match, there is no newline in the pattern space sed 's/a\nb/X/' file # pull the next line in first, then the newline exists sed 'N;s/a\nb/X/' file # a portable multi-line replacement: escape a real newline sed 's/,/\ /g' file # GNU and modern macOS sed also accept \n on the right sed 's/,/\n/g' file
\nin the replacement is a modern convenience. It works in GNU sed and in current macOS sed, but older BSD sed emitted a literalnand silently corrupted your data. The escaped-real-newline form above works everywhere.
Substitution gotchas
# greedy: .* runs to the LAST semicolon on the line printf 'a=1;b=2;c=3\n' | sed 's/=.*;/=X;/' # a=X;c=3 # non-greedy does not exist, use a negated class printf 'a=1;b=2;c=3\n' | sed 's/=[^;]*;/=X;/' # a=X;b=2;c=3 # an empty regex reuses the LAST regex used, not "match nothing" printf 'cat\n' | sed '/cat/s//dog/' # dog # & and \ are special in the replacement even when they arrive from a # variable, so pre-escape anything you interpolate val=$(printf '%s' 'A&B' | sed 's/[&\/]/\\&/g') printf 'cat\n' | sed "s/cat/$val/" # A&B # a shell variable containing / breaks the command, change the delimiter p=/usr/bin; printf 'X\n' | sed "s|X|$p|" # /usr/bin
- s/// reports nothing when it does not match, and the exit status stays 0. Add
-n ... p and check for empty output if you need to know.
- g and N count non-overlapping matches left to right.
- A zero-length match with g advances one character rather than looping forever:
sed 's/x*/-/g' inserts a dash between every character.