Skip to content
Latchkey

sed GNU vs BSD Differences in CI

sed scripts that pass on ubuntu-latest can fail on macos-latest because GNU and BSD sed differ in several specific places.

sed is not one tool. GNU sed ships on Linux runners, BSD sed ships on macOS runners, and a handful of features behave differently between them.

What it does

Both implementations follow the POSIX core for s, d, p, and addressing, so simple substitutions are portable. The differences show up in in-place editing, extended regex flags, the append and insert commands, and GNU-only escapes.

Common usage

Terminal
# Portable: write to a temp file instead of -i
sed 's/old/new/' config.yml > config.yml.tmp && mv config.yml.tmp config.yml

# Both accept -E for extended regex (modern BSD and GNU)
sed -E 's/[0-9]+/N/g' file

# GNU-only \b word boundary; BSD uses [[:<:]] and [[:>:]]
sed 's/\bword\b/X/' file        # GNU
sed 's/[[:<:]]word[[:>:]]/X/' file # BSD

Differences

FeatureGNU (Linux)BSD (macOS)
-i no backupsed -ised -i ''
Extended regex-E or -r-E only
a/i/c textone-line a\ text worksneeds backslash-newline form
Word boundary\b, \< \>[[:<:]] [[:>:]]
\n in replacementexpands to newlineoften literal n; use actual newline

In CI

When a job runs a matrix across ubuntu-latest and macos-latest, assume the lowest common denominator: -E for regex, no -i (redirect instead), and POSIX character classes over GNU escapes. Or install GNU sed on macOS with brew install gnu-sed and call gsed.

Common errors in CI

The classic macOS-only failure is sed: 1: "...": invalid command code, raised when BSD sed parsed a GNU-style -i argument or a\ block it did not expect. On Linux, a script written for BSD a\ insert may print nothing or error with sed: -e expression #1, char N: expected \ after a', c' or `i'.

Related guides

Run this faster and cheaper on Latchkey managed runners. Start free →