Skip to content

fix(builtins): test and [ reject the operands bash rejects - #2612

Merged
chaliy merged 1 commit into
mainfrom
claude/test-integer-errors
Oct 8, 2026
Merged

chaliy merged 1 commit into
mainfrom
claude/test-integer-errors

Conversation

@chaliy

@chaliy chaliy commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

What changed

test and [ now treat an operand bash cannot use as an error instead of
guessing a value:

  • a non-integer operand of -eq/-ne/-lt/-le/-gt/-ge reports integer expression expected and exits 2
  • a two-word expression whose first word is not a unary operator reports unary operator expected
  • an unknown three-word operator reports binary operator expected
  • four or more operands report too many arguments
  • -a and -o evaluate both sides, because bash's test connectives do not short-circuit

An integer operand may carry surrounding whitespace, a sign and leading zeros,
but not a base prefix, an exponent, or a value past 64 bits — same as bash.

Why

The old evaluator parsed any non-numeric operand as 0 and answered the
comparison, so a typo or an unset variable silently flipped a condition. The
worst case is a false positive:

[ a -lt 2 ] && echo "guard passed"

bash writes [: a: integer expression expected, exits 2 and the guard does not
pass. Bashkit printed guard passed.

Before / After

expression bash 5.2 bashkit before bashkit after
test 1 -eq abc test: abc: integer expression expected, 2 (silent), 1 test: abc: integer expression expected, 2
[ a -lt 2 ] [: a: integer expression expected, 2 (silent), 0 [: a: integer expression expected, 2
[ a -lt ] [: a: unary operator expected, 2 (silent), 1 [: a: unary operator expected, 2
test a foo b test: foo: binary operator expected, 2 (silent), 1 test: foo: binary operator expected, 2
test a b c d test: too many arguments, 2 (silent), 1 test: too many arguments, 2
[ 1 -eq 2 -a abc -eq 1 ] [: abc: integer expression expected, 2 (silent), 1 [: abc: integer expression expected, 2
test 0x10 -eq 16 test: 0x10: integer expression expected, 2 (silent), 1 test: 0x10: integer expression expected, 2
test " 5 " -eq 5 0 0 0

bash also prefixes the diagnostic with the script and line; the interpreter does
not track a script path, which is a separate known gap.

Risk

Medium: a script that relied on a bad operand comparing as 0 now fails loudly.
That is the point — bash fails there too — but it is a behavior change, not
only a message change. [[ ... ]] is untouched and still answers false.

The evaluator's return type changed from bool to Result<bool, TestError>,
so every branch now states explicitly whether it is an answer or an error. One
unit test asserted the old parse-as-zero behavior and was rewritten.

Checklist

  • cargo test -p bashkit --lib (2908 passed)
  • cargo test -p bashkit --test integration (1351 passed)
  • 10 new spec cases verified against bash 5.2
  • bash_comparison differential tests against real bash
  • cargo clippy -p bashkit --all-targets -- -D warnings
  • cargo fmt --all --check
  • cargo test -p bashkit-cli (56 passed)
  • just check-okf
  • Stack probe: 1992 KB, inside the two-MiB TM-DOS-020 budget
  • Knowledge updated (log.md)

https://claude.ai/code/session_019aFikmptPc91Fj4N2iDXQA


Generated by Claude Code

@chaliy chaliy self-assigned this Oct 7, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit 276a244 Commit Preview URL

Branch Preview URL
Oct 08 2026, 02:28 AM

A non-integer operand of -eq and friends parsed as 0, so `[ a -lt 2 ]`
answered true. It is a usage error in bash: the shell says
`test: a: integer expression expected` and exits 2. The same applies to
a two-word expression whose first word is not a unary operator, an
unknown three-word operator, and four or more operands. `-a` and `-o`
now evaluate both sides, since bash's connectives do not short-circuit.

Claude-Session: https://claude.ai/code/session_019aFikmptPc91Fj4N2iDXQA
@chaliy
chaliy force-pushed the claude/test-integer-errors branch from 5317263 to 276a244 Compare October 8, 2026 02:27
@chaliy
chaliy merged commit 0e96f9b into main Oct 8, 2026
46 checks passed
@chaliy
chaliy deleted the claude/test-integer-errors branch October 8, 2026 02:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant