[BUG] Bash tool: injected grep/find shell functions terminate the calling shell under bash < 4.0 (macOS system /bin/bash)
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report (please file separate reports for different bugs)
- [x] I am using the latest version of Claude Code
What's Wrong?
In native builds, the per-session shell snapshot injects wrapper functions that shadow grep and find (redirecting them to the embedded ugrep/bfs). Each function decides how to invoke the embedded tool with [[ $BASHPID != $$ ]]:
function find {
...
if [[ -n $ZSH_VERSION ]]; then
ARGV0=bfs "$_cc_bin" -S dfs -regextype findutils-default "$@"
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
ARGV0=bfs "$_cc_bin" ... "$@"
elif [[ $BASHPID != $$ ]]; then # $BASHPID is empty on bash < 4.0
exec -a bfs "$_cc_bin" ... "$@" # => always taken => replaces the shell
else
(exec -a bfs "$_cc_bin" ... "$@") # correct branch, never reached
fi
}
$BASHPID was introduced in bash 4.0. Under bash 3.2 it expands to the empty string, so [[ "" != "<pid>" ]] is always true and the bare exec branch runs even in the main shell. exec replaces the calling shell, so every command sequenced after grep/find in the same shell (; …, && …, brace groups) is silently dropped. Both injected functions are affected.
This is not a fringe configuration. /bin/bash on macOS has been frozen at 3.2.57 since 2007 (Apple ships no GPLv3 software) and is the system bash on every current Mac. Any user whose $SHELL is /bin/bash runs the Bash tool under bash 3.2 — common on machines set up before the zsh default, in CI, or by preference.
What Should Happen?
Commands sequenced after grep/find should run normally; the wrapper must never replace the user's main shell.
1. Fix the guard by defaulting $BASHPID to $$, so it collapses to the safe subshell branch when $BASHPID is unavailable and preserves the bare-exec optimization on bash 4.0+:
elif [[ ${BASHPID:-$$} != $$ ]]; then
On bash < 4.0 the comparison is false and the always-correct ( exec … ) branch runs; on 4.0+ behavior is unchanged. Apply to every wrapper using this guard (grep, find, and any future ones).
2. Provide a supported opt-out for the shadowing. The Bash tool monkey-patches the user's shell with functions that shadow grep/find; this is invasive, depends on shell internals (this bug is one failure mode), and currently can't be turned off. The earlier builtin-ripgrep path honored USE_BUILTIN_RIPGREP=0, but a similar escape hatch was never added for the embedded bfs/ugrep functions. A documented env var (a USE_BUILTIN_RIPGREP-style flag, or CLAUDE_CODE_DISABLE_EMBEDDED_SEARCH=1) and/or settings key that suppresses these functions and falls back to system grep/find (and rg when present) would:
- Make failures like this debuggable — isolating it would be a one-line experiment (disable shadowing → the silent truncation disappears → cause confirmed) and provide an immediate workaround while a fix ships. As things stand, this problem was EXTREMELY confusing to track down!
- Respect users who prefer their own toolchain.
- Bound the blast radius of any future shell-shadowing bug to a supported switch rather than a required release.
Error Messages/Logs
None — and that is central to the severity. The command succeeds (exit 0) and produces correct output; the shell is simply gone afterward, with no error, warning, or nonzero status. Observed vs. expected:
$ /bin/bash -c 'echo BEFORE; [[ $BASHPID != $$ ]] && exec true; echo AFTER'
BEFORE
# expected: "BEFORE" then "AFTER"; actual: "AFTER" missing — exec replaced the shell
Steps to Reproduce
- On macOS, ensure
$SHELL=/bin/bash(system bash 3.2.57). Minimal check, guard in isolation:
``sh`
/bin/bash -c 'echo BEFORE; [[ $BASHPID != $$ ]] && exec true; echo AFTER'
BEFORE`.
→ prints only
- In Claude Code (native build, ≥ ~2.1.117) with that shell, run any Bash tool call that sequences work after
find/grep:
``sh`
echo BEFORE; find . -maxdepth 1 >/dev/null; echo AFTER
BEFORE
→ prints only ; AFTER (and anything after find`) silently does not run.
Claude Model
Opus
Is this a regression?
Yes, this worked in a previous version
Last Working Version
2.1.116
Claude Code Version
2.1.150 (Claude Code)
Platform
Anthropic API
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
This report was drafted with Claude's help. This appears to be standard practice in this repository, but I wanted to call it out because I do not normally use LLMs in my written communication.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗