The way to write a command so every target runs it: bash 3.2 on macOS, GNU and BSD userlands, busybox, dash as /bin/sh, Git Bash on Windows, and fish and zsh where a file is shared with them. Each row is a spelling that has been gotten wrong here at least once. Write is what to use, Not what it replaces, Breaks on the target that fails and how, and Caught by what stops it coming back: a lint row (drift’s tables, dialects, shellcheck, shfmt), a suite, or - where no pattern can tell the right use from the wrong one - review, with the failure it was learned from.
Why a construct looks the way it does is GLOSSARY.md’s job; a row here links the entry rather than restating it. A new row belongs here the first time a spelling costs a CI round trip.
sed -r, or a backslash-bar alternation in a basic sed
BSD sed (macOS, FreeBSD, OpenBSD): -r is unknown and backslash-bar is a literal bar, so the match silently fails
drift
sed … >"$tmp" then _hi_write_back
sed -i
BSD takes an argument after -i, GNU does not (HI.08, HI.09)
drift
grep -E 'a\|b', or -e a -e b
a backslash-bar alternation in a basic grep
BSD grep (OpenBSD): backslash-bar is a literal bar, so nothing matches
drift
grep -E
grep -P
BSD and busybox grep have no PCRE
drift
sed '$d' to drop the last line
head -n -1
BSD and busybox head take no negative count
drift
awk as POSIX writes it
gawk’s gensub, strftime, \y, arrays of arrays
mawk, busybox awk, and onetrue (BSD) awk; the include scan is checked against all four
drift (the gawk-only functions); the include scan is run under all four in review
Pipes under strict mode
Write
Not
Breaks on
Caught by
printf '%s\n' "$x" \| grep -E pat >/dev/null
printf '%s\n' "$x" \| grep -q pat under pipefail
OpenBSD, intermittently: grep -q leaves at its first match, the writer gets SIGPIPE (141), and pipefail fails the pipeline - OpenBSD’s 1 KB stdio buffer turns a 1.4 KB input into two writes
review; learned from the OpenBSD job’s release_notes.sh --check case
an awk that sets a flag and reads to the end
an awk that exits part-way through a piped input
the same SIGPIPE, and set -e then ends the script
review; the same failure
grep -q pat "$file"
—
nothing: a file argument has no writer to kill
—
strict mode on at the top of a sourced file, off at its end
leaving set -euo pipefail on in a file an interactive shell sources
the user’s shell dies on the next non-zero status (HI.15)
drift
Output
Write
Not
Breaks on
Caught by
printf '%s\n' "$x"
echo -e, or echo of a backslash
dash and macOS’s POSIX-mode sh expand escapes that bash-as-sh prints as text