#922 replaced roughly 28 ... | grep -q PAT tests with [ "$(grep -c PAT ... || true)" != 0 ], which fixed a real EPIPE race (#486). The replacement answers present where the original answered absent when grep produces no stdout, and a broken pattern is the way to get there.
Measured
new idiom old idiom
pattern present, valid ERE present present
pattern absent, valid ERE absent absent
INVALID ERE (grep rc=2) present absent <- inverted
The mechanism is that grep -c prints nothing on a usage error, not 0:
grep -cE '[' file -> stdout is [] (empty)
[ "" != 0 ] -> TRUE (string comparison: "" is not "0")
So the helper reports the pattern PRESENT for a question it never managed to ask.
Why it is worth a line, even though nothing is broken today
Every pattern in the tree is valid right now, so no site is wrong. The hazard is the direction of the next edit. A premise arm phrased to want present — and most of them are, because a premise asserts the fixture really is in the state the test needs — turns GREEN when its pattern stops compiling. The arm passes because the instrument broke, which is the failure this repository spends most of its effort refusing elsewhere.
The old form failed the other way: a broken pattern reported absent and the arm went red, which is loud.
What I did not find
I could not make an unreadable file produce the inversion: grep -c on a file it cannot read still prints 0, so that case answers absent and agrees with the old form. (My first attempt at that test was invalid anyway — I ran it as root, which bypasses the mode bits.) So the reachable trigger is a pattern that does not compile, not an I/O failure.
Suggested shape of a fix
Make the empty case loud rather than truthy. Either compare numerically, so an empty value is a hard error instead of a silent present:
[ "$(grep -c PAT ... || true)" -gt 0 ] # "integer expression expected" on empty
or default it explicitly:
_n="$(grep -c PAT ... || true)"; [ "${_n:-0}" != 0 ]
The second keeps the current semantics and makes the intent visible; the first turns a broken pattern into a failure, which matches how the rest of the harness treats an instrument that could not run.
Found while reviewing the #486 sweep for #926. Reported independently by @linuxhikerpm, whose version also named unreadable input — that half does not reproduce, per above.
#922 replaced roughly 28
... | grep -q PATtests with[ "$(grep -c PAT ... || true)" != 0 ], which fixed a real EPIPE race (#486). The replacement answers present where the original answered absent when grep produces no stdout, and a broken pattern is the way to get there.Measured
The mechanism is that
grep -cprints nothing on a usage error, not0:So the helper reports the pattern PRESENT for a question it never managed to ask.
Why it is worth a line, even though nothing is broken today
Every pattern in the tree is valid right now, so no site is wrong. The hazard is the direction of the next edit. A premise arm phrased to want
present— and most of them are, because a premise asserts the fixture really is in the state the test needs — turns GREEN when its pattern stops compiling. The arm passes because the instrument broke, which is the failure this repository spends most of its effort refusing elsewhere.The old form failed the other way: a broken pattern reported
absentand the arm went red, which is loud.What I did not find
I could not make an unreadable file produce the inversion:
grep -con a file it cannot read still prints0, so that case answersabsentand agrees with the old form. (My first attempt at that test was invalid anyway — I ran it as root, which bypasses the mode bits.) So the reachable trigger is a pattern that does not compile, not an I/O failure.Suggested shape of a fix
Make the empty case loud rather than truthy. Either compare numerically, so an empty value is a hard error instead of a silent
present:or default it explicitly:
The second keeps the current semantics and makes the intent visible; the first turns a broken pattern into a failure, which matches how the rest of the harness treats an instrument that could not run.
Found while reviewing the #486 sweep for #926. Reported independently by @linuxhikerpm, whose version also named unreadable input — that half does not reproduce, per above.