What
scripts/shell-portability-tokens.txt line 250 catches GNU-only in-place sed, but only when the
command word is written bare. A quoted or quote-spliced spelling invokes the same GNU-only option
after shell quote removal and goes unreported.
Verified by running the shipped pattern directly:
| input |
verdict |
sed -i "s/x/y/" f |
FLAGGED |
"sed" -i 's/x/y/' file |
clean |
s"e"d -ni p file |
clean |
Both quoted forms execute GNU sed -i with no suffix — the exact portability defect this token
exists to catch — and both pass the gate.
The same limitation applies to the long-form token on line 271
(sed … --in-place), which uses the identical (^|[^[:alnum:]_])sed anchor.
Root cause
The anchor requires the literal characters sed to appear as a word. Shell quote removal happens
before the command word is resolved, so s"e"d, "sed", 'sed', and \sed are all the same
command to the shell and none of them are the string sed in the source text. An ERE matching
source text cannot see through that.
Why this is filed rather than fixed
Widening the anchor to tolerate arbitrary quote splicing inside the command word means matching
something like s["'\\]*e["'\\]*d, which starts flagging ordinary prose and unrelated identifiers
containing those letters. That trades a rare false negative for common false positives, and this
gate's stated posture is the opposite — it accepts false negatives explicitly, in the same file:
It is a false NEGATIVE on a rare shape, the direction this gate already accepts everywhere else.
So the fix is not a pattern tweak. Closing this properly needs the gate to model shell word
splitting and quote removal — real parsing, which a grep-level tripwire deliberately does not
attempt. That is a design decision about what this gate is for, not a bug in a regex.
Options, if it is worth closing at all
- Accept and document. Add the quoted-command-word case to the file's existing list of known
false negatives, beside the nested-process-substitution note. Cheapest; makes the limit visible
to anyone auditing the gate's coverage rather than leaving it discoverable only by reading the
regex.
- Escalate the gate. Replace or supplement the grep tripwire with a real shell parser
(shellcheck already parses these files for other reasons and has its own opinions about
portability). Closes this class of gap entirely, at the cost of a heavier dependency in the lane.
- Leave as-is. Defensible: writing
s"e"d is pathological, and a reviewer would question it on
sight for reasons unrelated to portability.
Recommend option 1 unless someone has independently wanted option 2 for other reasons — the
value here is an accurate record of what the gate does and does not catch, not the rare shape itself.
Provenance
Raised by chatgpt-codex-connector on #2085, where it was misattributed to that PR. #2085 is
comment-only — its whole diff is 18 lines of # text — and the pattern it ships is byte-identical
to the one on main. The finding is correct on substance and wrong about cause, so it is recorded
here instead of blocking a documentation change that neither introduced nor touched it.
Related
What
scripts/shell-portability-tokens.txtline 250 catches GNU-only in-placesed, but only when thecommand word is written bare. A quoted or quote-spliced spelling invokes the same GNU-only option
after shell quote removal and goes unreported.
Verified by running the shipped pattern directly:
sed -i "s/x/y/" f"sed" -i 's/x/y/' files"e"d -ni p fileBoth quoted forms execute GNU
sed -iwith no suffix — the exact portability defect this tokenexists to catch — and both pass the gate.
The same limitation applies to the long-form token on line 271
(
sed … --in-place), which uses the identical(^|[^[:alnum:]_])sedanchor.Root cause
The anchor requires the literal characters
sedto appear as a word. Shell quote removal happensbefore the command word is resolved, so
s"e"d,"sed",'sed', and\sedare all the samecommand to the shell and none of them are the string
sedin the source text. An ERE matchingsource text cannot see through that.
Why this is filed rather than fixed
Widening the anchor to tolerate arbitrary quote splicing inside the command word means matching
something like
s["'\\]*e["'\\]*d, which starts flagging ordinary prose and unrelated identifierscontaining those letters. That trades a rare false negative for common false positives, and this
gate's stated posture is the opposite — it accepts false negatives explicitly, in the same file:
So the fix is not a pattern tweak. Closing this properly needs the gate to model shell word
splitting and quote removal — real parsing, which a grep-level tripwire deliberately does not
attempt. That is a design decision about what this gate is for, not a bug in a regex.
Options, if it is worth closing at all
false negatives, beside the nested-process-substitution note. Cheapest; makes the limit visible
to anyone auditing the gate's coverage rather than leaving it discoverable only by reading the
regex.
(
shellcheckalready parses these files for other reasons and has its own opinions aboutportability). Closes this class of gap entirely, at the cost of a heavier dependency in the lane.
s"e"dis pathological, and a reviewer would question it onsight for reasons unrelated to portability.
Recommend option 1 unless someone has independently wanted option 2 for other reasons — the
value here is an accurate record of what the gate does and does not catch, not the rare shape itself.
Provenance
Raised by
chatgpt-codex-connectoron #2085, where it was misattributed to that PR. #2085 iscomment-only — its whole diff is 18 lines of
#text — and the pattern it ships is byte-identicalto the one on
main. The finding is correct on substance and wrong about cause, so it is recordedhere instead of blocking a documentation change that neither introduced nor touched it.
Related