sed Cheatsheet
Basics and Invocation
Use this sed reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
The execution model
sed reads one line at a time into the pattern space, runs the whole script against it, prints the pattern space, and repeats. That cycle explains almost every surprising behavior.
read line -> strip trailing newline -> run every command in order -> auto-print pattern space (unless -n) -> next line
sed 's/old/new/' file # substitute, print everything sed -n '/error/p' file # print only what you ask for sed -n '5,10p' file # a line range sed '/^#/d' file # delete comment lines sed -e 's/a/b/' -e 's/c/d/' file # two commands, applied in order sed -f script.sed file # commands from a file
The hold space is a second buffer that survives between cycles. It starts empty
and only the h H g G x commands touch it.
Command-line options
| Option | Meaning | Portable? |
|---|---|---|
-n | no auto-print, you drive output with p | POSIX |
-e script | append a script fragment, repeatable | POSIX |
-f file | read a script from a file, repeatable | POSIX |
-i[ext] | edit in place, and the syntax differs by platform | neither, see below |
-E | extended regex (ERE) | POSIX since 2018, works in both |
-r | ERE, GNU spelling. macOS documents it as an alias for -E | avoid, use -E |
-s | treat each file separately, so $ is per file | GNU only |
-z | records are NUL-separated, not newline | GNU only |
-u | unbuffered output | both, not POSIX |
-l | BSD: line-buffered output. GNU: -l N sets the l wrap width | same letter, different meaning |
--posix | disable GNU extensions | GNU only |
--debug | dump the parsed program and each cycle | GNU only |
--help --version | information | GNU only (BSD errors) |
sed --version # GNU prints "sed (GNU sed) 4.10", BSD prints a usage errorThat error is the fastest way to tell which sed you have.
The one gotcha that breaks every script
-i takes a mandatory backup suffix on BSD/macOS and an optional attached
one on GNU. There is no spelling that means "in place, no backup" on both.
sed -i 's/a/b/' f # GNU: edits f. BSD: reads "s/a/b/" as the suffix, then # tries to run "f" as the script and fails sed -i '' 's/a/b/' f # BSD: edits f. GNU: reads "" as the script, then # tries to open "s/a/b/" as a file and fails sed -i'' -e 's/a/b/' f # GNU: fine. BSD: writes a backup called f-e
Pick one of these instead:
sed -i.bak 's/a/b/' f && rm -f f.bak # works on both perl -i -pe 's/a/b/' f # works everywhere, no suffix dance sed 's/a/b/' f > f.tmp && mv f.tmp f # explicit, and you can diff first gsed -i 's/a/b/' f # brew install gnu-sed, then use gsed
Regex flavor
sed uses BRE by default and ERE with -E. Perl syntax is not available in
either.
| Construct | BRE | ERE (-E) |
|---|---|---|
| group | \(...\) | (...) |
| alternation | | GNU only | | |
| one or more | \+ GNU only | + |
| zero or one | \? GNU only | ? |
| interval | \{n,m\} | {n,m} |
| backreference in the pattern | \1 | \1 GNU only |
\w \s \b \< \> | GNU only | GNU only |
[[:alpha:]] [[:digit:]] | ✓ | ✓ |
printf 'aaa\n' | sed 's/a\+/X/' # GNU: X. BSD: unchanged, \+ is literal printf 'aaa\n' | sed -E 's/a+/X/' # X on both printf 'abab\n' | sed -E 's/(ab)\1/X/' # GNU: X. BSD: unchanged printf 'abab\n' | sed 's/\(ab\)\1/X/' # X on both printf 'a b\n' | sed 's/[[:space:]]/_/' # a_b on both
Use
-Eand the POSIX character classes and you will write regexes that behave the same on a Mac and in CI. Every\w,\s,\+, and\|is a portability bug waiting for a Linux runner.
Exit status and errors
| Status | Meaning |
|---|---|
0 | success |
1 | bad usage or a bad script. BSD also uses 1 for an unreadable file |
2 | GNU only: a file could not be read (4 for an I/O error) |
N | GNU only: whatever qN or QN set |
# GNU only: use sed as a test, exit 1 when the pattern is absent gsed -n '/needle/{s//&/;q0}; $q1' haystack.txt # portable: let grep do the testing grep -q needle haystack.txt
sed does not report "no substitutions made" through its status, on either
platform. A s/// that matched nothing still exits 0.