The negated-assertion audit flags a ! that belongs to find, not to the shell. A line like find dir \( ! -type d -o -name x \) fails hack/bats-no-negated-assert.bats, but that ! is a find operand and errexit has nothing to exempt.
The audit splits each logical line into segments and then checks whether a segment starts with !. The split pattern in cnb_scan has \( in its separator class, and that also matches the escaped \( find needs. So the text after it becomes its own segment, starting with ! -type d. By the same pattern a ! right after the \; of find -exec ... \; would be flagged too.
This turned the unit tests on main red on 2026-10-05, when hack/select-e2e_test.bats got \( ! -type d. #4751 rewrites that line as -not -type d. This gets past the audit but leaves it as is, so the next \( ! fails the same way.
I'd skip a (, ) or ; preceded by a backslash when splitting, and add a find ... \( ! -type d ... \) case to the audit's own tests, next to the root and recursion cases. Quoted forms like '(' probably need the same handling, I have not checked them.
The negated-assertion audit flags a
!that belongs to find, not to the shell. A line likefind dir \( ! -type d -o -name x \)failshack/bats-no-negated-assert.bats, but that!is a find operand and errexit has nothing to exempt.The audit splits each logical line into segments and then checks whether a segment starts with
!. The split pattern incnb_scanhas\(in its separator class, and that also matches the escaped\(find needs. So the text after it becomes its own segment, starting with! -type d. By the same pattern a!right after the\;offind -exec ... \;would be flagged too.This turned the unit tests on main red on 2026-10-05, when
hack/select-e2e_test.batsgot\( ! -type d. #4751 rewrites that line as-not -type d. This gets past the audit but leaves it as is, so the next\( !fails the same way.I'd skip a
(,)or;preceded by a backslash when splitting, and add afind ... \( ! -type d ... \)case to the audit's own tests, next to the root and recursion cases. Quoted forms like'('probably need the same handling, I have not checked them.