Bash tool `grep` (bundled ugrep) silently misses `^` alternatives: `(a|b|^)x` and `(^|[^0-9])x` match nothing that GNU grep matches

Status Open
Reported on v2.1.296
Maintainer reply None cached
Activity 0 comments · opened Oct 11, 2026

Environment: Claude Code 2.1.296, Linux aarch64, bash. Inside the Bash tool, grep is the shell function Claude Code
injects, which runs the bundled ugrep 7.8.4 (ugrep --version through the same binary).

What happens: some -E patterns with ^ inside a group return no match, with exit status 1 and no warning, where
GNU grep matches. An agent that counts or checks absence with such a pattern gets a wrong answer it cannot detect.

printf 'fail 1\nx; fail 2\nfoo\n' > f.txt
grep -cE '(a|b|^)fail' f.txt            # 0   (GNU grep: 1)   ^ as the third alternative
grep -cE '(;|&&|^)fail' f.txt           # 0   (GNU grep: 1)
grep -cE '(a|^|b)fail' f.txt            # 1   (GNU grep: 1)   ^ first or second works
command grep -cE '(a|b|^)fail' f.txt    # 1   (/usr/bin/grep)

printf '5798-72bbf60eb5b8.json\n' > g.txt
grep -cE '(^|[^0-9])5798' g.txt         # 0   (GNU grep: 1)   the shape in #98421

Where the bug is: in ugrep's regex engine, not in Claude Code's wrapper flags. Running the bundled binary as
ugrep -E alone (without the wrapper's -G --ignore-files --hidden -I --exclude-dir=...) gives the same 0, and
upstream ugrep v7.8.5 (the latest release, built from source) gives the same results for every pattern above. $ in
the same positions works. Filed upstream as https://github.com/Genivia/ugrep/issues/573.

Why it matters here: the shadowing is silent. A user or an agent that types grep expects GNU grep's dialect; the
injected function swaps the engine without saying so, so a wrong count looks like a real answer. In our case a count
recorded from the Bash tool was wrong for weeks, always by exactly the line-initial matches, until someone re-ran it with
/usr/bin/grep.

Asks (any of these would have prevented it):

  1. Document in the Bash tool's description (and /help) that grep is ugrep, and that command grep reaches the

system grep.

  1. An opt-out for the grep/find shadow functions (#69736, #91040).
  2. Until ugrep is fixed upstream, fall back to the system grep for -E/-G patterns that put ^ in a group (or warn).
  3. Bump the bundled ugrep once upstream ships a fix.

Related: #98421 (same engine family, (^|[^0-9]) shape), #69736 and #91040 (opt-out requests), #80103 (bundled search
wrappers narrow silently and exit 0).

View original on GitHub ↗