Skip to content

check-bash32-floor's construct table has &>> (append-both) but not |& (pipe-both) — both are bash 4.0, so one is caught and the other is not #12634

Description

@os-litant

scripts/check-bash32-floor.mjs carries a construct table of bash-4+ spellings it refuses in this repo's tracked shell. It has an append-both entry for the & before a doubled > redirection (bash 4.0). It has no entry for |&, the pipe-both operator, which arrived in the same bash release and fails the same way on the declared 3.2 floor.

Found incidentally while fixing #12518 — the gate correctly caught an append-both spelling I had written into scripts/pm/os-verify-lock.sh's self-test, and the neighbouring pipe-both spelling in the very same block sailed through. Unrelated to that card's surface, so filed rather than fixed.

Measured

The table's ids, read off the source:

$ grep -n "id:" scripts/check-bash32-floor.mjs
189: mapfile 211: nameref-global 233: wait-n 257: fd-autoalloc
200: assoc-array 222: coproc 244: shopt-4 268: case-modify
279: param-transform 290: negative-subscript 301: case-fallthrough
312: has-v 323: printf-time 334: append-both 345: epoch-vars

Fifteen constructs, append-both among them, no pipe-both. The gate's own green line confirms the count: "15 constructs checked, floor bash 3.2".

Both operators are bash 4.0 (|& is listed in the bash 4.0 NEWS as a new operator; the gate already dates append-both to 4.0 in its own message). On bash 3.2:

  • cmd &>> log parses as & then >> — the command is backgrounded. The gate says exactly this, and it is why the entry exists.
  • cmd |& next is a syntax error — the script does not run at all.

The second is arguably the louder failure of the two, and it is the one nothing checks.

Why it matters here rather than in the abstract

The gate's value is that it fires on a host nobody develops on: "Your local run and CI both pass because both run bash 5. That is the point of this gate." A gap in the table is therefore invisible by construction — no local run and no CI run can reveal it, which is the same reason the class survived long enough to need the gate in the first place (#12221).

It also fires on quoted occurrences, not only command position — measured on the first draft of the #12518 change, where the flagged lines were an st_case label and a single-quoted payload string handed to bash -c. So the coverage asymmetry is not narrow: any tracked shell file mentioning either operator is judged by whether the table happens to know it.

Suggested shape (not prescribing)

A pipe-both entry alongside append-both, with the same self-test pair the table's other entries carry (the pattern matches a real use; the pattern does NOT match its 3.2 replacement 2>&1 |). Worth a sweep of the remaining bash 4.0 operator set at the same time rather than one entry at a time — the table is a denylist, so anything absent reads as approved.

Filed unassigned for triage; no fix attempted here.


Generated by Claude Code

Metadata

Metadata

Assignees

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions