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
# 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 # BSDDifferences
| Feature | GNU (Linux) | BSD (macOS) |
|---|---|---|
| -i no backup | sed -i | sed -i '' |
| Extended regex | -E or -r | -E only |
| a/i/c text | one-line a\ text works | needs backslash-newline form |
| Word boundary | \b, \< \> | [[:<:]] [[:>:]] |
| \n in replacement | expands to newline | often 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'.