[MODEL] Frequently uses Bash tools (sed/grep/etc) when use-case is well aligned to other builtin tools (Read/Grep/etc)
Preflight Checklist
- [x] I have searched existing issues for similar behavior reports
- [x] This report does NOT contain sensitive information (API keys, passwords, etc.)
Type of Behavior Issue
Other unexpected behavior
What You Asked Claude to Do
See attached (redacted, and line-wrapped for readability) for a full exchange analyzing occurrences based on claude/agent logs. The analysis ran afoul of some of the same issues being analyzed.
What Claude Actually Did
I frequently see Bash tool permission requests invocations just like the following:
# Output lines 147-162 from a given file.
sed -n 147,162p /path/to/some/file
# Search for a pattern in a directory.
grep -r "^some pattern$" /path/to/some/dir
# Write a file from content expressed via heredoc.
cat > file.txt <<EOF ... EOF
Often these are just solitary commands (not as part of a pipeline). Sometimes, e.g. grep, might be piped to head. Sometimes these &&'d commands like:
cat > file.py <<EOF ... EOF && chmod +x file.py && ./file.py
Expected Behavior
All of the above examples are undesirable.
These cases should broadly use the Read, Grep (builtin, not Bash(grep)), and Write/Edit.
The &&'d example above ideally should use Write followed by Bash tool call(s).
Files Affected
N/A
Permission Mode
Accept Edits was ON (auto-accepting changes)
Can You Reproduce This?
Sometimes (intermittent)
Steps to Reproduce
Sufficiently long code-investigation/debugging sessions will usually involve seeing many permission prompts matching these undesirable patterns.
Claude Model
Sonnet
Relevant Conversation
N/A
Impact
Medium - Extra work to undo changes
Claude Code Version
v2.1.2
Platform
AWS Bedrock
Additional Context
I'll let Claude's own analysis conclusions explain why this matters (everything that follows is from Claude own reflection)...
Why This Matters
Built-in tools (Read, Grep, Write, Edit) are specifically designed to work efficiently with Claude Code's permission system. Using bash commands bypasses these optimizations, resulting in slower task completion and more human time spent on reviews. The permission system can cache approvals for built-in tool operations but cannot cache unique bash command content like heredocs, creating significant efficiency penalties in human-in-the-loop workflows.
Key Findings
1. sed -n for line ranges (45 cases, ~40 problematic)
When users mention line ranges (e.g., "lines 253-343" or "around line 150"), Claude agents often reach for sed -n because it directly translates the user's language. The Read tool requires offset/limit arithmetic (offset=252, limit=91), creating cognitive overhead. The root issue is parameter mismatch: users think in line numbers, but the Read tool speaks in offsets and limits. Solution: Add start_line/end_line parameters to the Read tool so agents can write Read(start_line=253, end_line=343) directly, eliminating the mental translation step and making the tool as intuitive as sed.
2. grep for searching (45 cases, ~30 problematic)
Claude agents use bash grep commands for operations like counting matches (grep -c), showing context lines (grep -A/-B/-C), or recursive searching (grep -r) because these features aren't obviously discoverable in the Grep tool. While the Grep tool supports all these capabilities (output_mode="count", -A=N, -B=N, -C=N parameters), they're buried in documentation. Agents fall back to familiar bash idioms because grep -c feels more direct than Grep(output_mode="count"). Solution: Enhanced system prompt with concrete examples showing how common grep patterns translate to Grep tool calls, and improved tool descriptions that prominently highlight these features.
3. cat heredoc for file creation (127 cases, ~127 problematic)
This is the most critical pattern because it represents a fundamental permission efficiency problem. Claude agents believe that writing file content inline with a heredoc bash command is more efficient, but this is a false efficiency heuristic. The permission system cannot cache heredoc approvals because each heredoc contains unique content, requiring full human review every time. Write tool operations complete reviews more efficiently and benefit from permission caching. When files need iteration (common for debugging scripts or refining content), heredocs require full reviews for every revision, while Edit tool presents diffs that dramatically reduce human review time for subsequent revisions. This applies to ALL file types: scripts, documentation, test fixtures, configs, temporary files. Solution: System prompt guidance emphasizing that Write/Edit tools are strictly more efficient for ALL file creation due to permission system architecture, particularly when considering human-in-the-loop review time.
Recommended Actions
Read tool enhancement:
- Add
start_lineandend_lineparameters - Keep existing
offset/limitfor compatibility - Example:
Read(file_path="/path/to/file", start_line=253, end_line=343)
System prompt updates:
- Add user language translation guide: "lines X-Y" → Read tool with start_line/end_line
- Add permission efficiency guidance: Write/Edit for ALL file creation (never cat heredoc)
- Explain why: permission caching and diff-based reviews make Write/Edit more efficient for human-in-the-loop workflows
- Add Grep tool feature examples: counting, context lines, recursive search
Tool description improvements:
- Grep tool: Prominently show output_mode="count" and context parameters
- Read tool: Explain start_line/end_line as intuitive alternative to offset/limit
46 Comments
I think it is time to BAN claude from using those tools. Making it understand the tools tailored for LLM agents and make those tools full-featured is the correct path. "bash for everything" is just wrong.
I run into the same issue all the time. It'd be great to fix this.
This is becoming a major problem.
Sometimes if I tell it to remember to not use sed (in CLAUDE.md or whatever) then it tries to be more creative and writes its own node scripts to search and replace tabs vs spaces for example 😂
Hilarious, but very dumb and inefficient and not leading to anything.
Engineer reported this after having installed the
superpowersplugin from marketplace, not sure if they are related at all, seeing this as a more frequent issues this past few days.It also enters a mode to ask for permission to run the command because is potentially dangerous. Like a grep with absolute path forward errors to /dev/null pipe head or pipe tail. So there needs to be a review, don't understand why it would default to a "escape sandbox" behaviour instead of following the guardrails.
Just ran into this issue as well, fwiw.
Same for me. I often need to tell Claude to use its own Read util.
I use AWS Bedrock, and no
superpowersplugin, btw.In case it's useful to others facing this issue, I've asked Claude to add a hook to automatically deny them. It added this hook to my project:
I'm not sure the
lsresponse totally make sense, since it wouldn't able to call it anymore even if I explicitly ask it to. But for me it does the job for now.Thanks @fgascon , I got claude to extend that script a little, seems to be working well at preventing find, grep etc...
@extemporalgenome For the
greppiped toheadpattern mentioned in the analysis, I've been using:This flattens output to a single line with
[N]markers, so subsequent| head -n 20calls don't actually truncate—they just return the entire line since there's only one.Doesn't address the preference for bash over built-in tools, but it prevents the "head limit was too small" repetition loops when agents do use bash+pipe patterns.
The hook approach from @fgascon and @dcerisier is the right user-side workaround. One gap worth noting: simple
split(" ")[0]parsing won't catch commands embedded in pipes or chains. For example,echo foo | grep patternpasses through because the first token isecho, notgrep.Here's a version that checks all segments of piped/chained commands:
Hook config in
settings.json:This catches
grepeven after a pipe (cmd | grep) or in chained commands (cmd && grep). It also handles path-prefixed commands (/usr/bin/grep) and env-var prefixes (LANG=C grep).That said, this is a band-aid — the root cause is in model behavior. The built-in tools already cover nearly every use case (Read supports
offset/limit, Grep supports-A/-B/-Ccontext andoutput_mode="count", Glob handlesfindpatterns), but the model doesn't consistently reach for them."That said, this is a band-aid — the root cause is in model behavior."
Just came to say +1 on this. As a heavy user of Claude models in harnesses other than Claude Code, I noticed this a long time ago. Built a slash command whenever I saw this occur. All it really does is send the following on /tool-use:
Always prioritize using the tools you have over equivalent terminal commands.Always thought perhaps poorly optimized harnesses were to blame. Turns out Claude itself needs to be better about this 🙂.
Adding concrete evidence to this — I hit the same behavior on Windows 11 with claude-opus-4-6 (1M context).
What makes this particularly notable is that Claude Code's own system prompt explicitly prohibits it:
Yet in my session, Claude used
cat,grep, andfindvia Bash for file reading and searching despite having Read, Grep, and Glob available. This isn't just a preference issue — the model is actively violating its own instructions.I filed #39979 about this but closing it in favor of this issue since it covers the same core problem. Hopefully the system prompt angle adds useful context for the fix.
Adding evidence from #39979 (consolidating here per @mimuelas's suggestion).
The system-prompt-violation angle: Claude Code's own system prompt explicitly says _"Do NOT use the Bash to run commands when a relevant dedicated tool is provided"_ and lists specific mappings (Read instead of cat, Grep instead of grep, Glob instead of find, Edit instead of sed). Despite this, the model violates this instruction at a roughly 40% rate in our measurement across 200+ sessions on a 2M LOC C++ codebase (Windows 11, Opus 4.6).
When it's worst:
grep -r pattern dir | head -20has no single dedicated-tool equivalent, so the model generalizes and uses Bash for simple cases too.What doesn't work: CLAUDE.md rules. We have explicit instructions reinforcing the system prompt prohibition. Compliance is ~100% for the first ~30 minutes, then degrades. The model will sometimes _quote_ the rule in its thinking block and then violate it in the same response.
What does work (partially): The PreToolUse hook approach shared by @yurukusa and others in this thread. We haven't deployed it yet but the pattern is sound — deny Bash calls that match standalone
cat,grep,find,head,tailand force the model to retry with the dedicated tool.The hooks are a good workaround but the root cause is model behavior — this needs to be addressed in training or prompting, not bolted on by users.
FWIW - Using Claude Sonnet and Opus in OpenCode via API access has a far better tool adherence than what I'm seeing in Claude Code. To me, that indicates it's the Claude Code harness and perhaps a bug in their context management strategy.
The Claude Code editor in VSCode sometimes shows three (3) tabs around code when doing an Edit operation, but in the actual file there is only two (2) tabs. And 4 tabs when there is 3 etc. Those operations always fail, and then it turns to
sed.even with the pretooluse hook, I see Claude doubling down and doing what it wants.
I have found when denying the affect bash/sed/awk commands, Claude gets completely sidetracked from the original task.
Instead of using it's tools, it starts trying to understand why it doesn't have permission to run various bash commands. It starts looking through .claude/projects to discover ways to circumvent the bash permisisons denied.
These are a selection of calls it tries to make on it's road to discovery, it's persistent.
But something is going wrong. Why will it spend tons of tokens trying to figure this out instead of just obeying the deny and using the tools it has for searching, reading, writing etc.
Even starts writing for loops
... and python code to assist in it's discovery
@raldred works fine for me, but my deny inserts a message of why that says, the user requests that you use this other tool instead.
@Caleb-KS you're using a pre tool hook to inject the deny message?
@raldred that's right. I also have it tell claude it can rerun the exact same tool call and it will go through the second time. I've found if you couch it as, "the user prefers" rather than "dont do that", claude will respect that more instead of trying to defeat your mechanism.
I think there's something else going on with mine then because it refuses to believe it has the Grep tool.
LSP — workspaceSymbol, findReferences, goToDefinition for symbol-level lookups in Ruby code
Read — for known paths
Bash — last resort for free-text search
Grep/Glob appear to not be provisioned for this session. I'll lean on LSP and Read as much as possible, and only use Bash grep/rg when I genuinely need free-text search across unknown files
Just come across this
https://code.claude.com/docs/en/changelog#2-1-117
That might explain it.
ah, ok. That's good to know. I'll have to update my guidance hook.
I'm also seeing this with Claude Code running Opus 4.7 on Windows 11. Claude will often try running various bash tools instead of the built-in tools, causing unnecessary permission prompts. Claude suggested adding the following to CLAUDE.md.
\- Use the dedicated tools, not Bash equivalents: \\Glob** for finding files
(not
find), \\Grep** for searching contents (notgrep), \\Read** forinspecting a file (not \
cat). The dedicated tools integrate with thepermission UI and return clickable file links.
However, if this suggestion is already part of the system prompt and it's being ignored, I don't know how helpful it will be.
That seems to indeed be the explanation. On my Mac with Claude 2.1.154:
The read and search half of this got explained by the
ugrep/bfschange above. The write half hasn't.Across 25 of my own sessions (Claude Code 2.1.205 to 2.1.221, macOS, mostly
claude-opus-5, 11 Jul to 6 Aug 2026): 2606Edit, 274Write, and 362Bashcalls that write a repo file. So about 11% of file writes skip the file editing tools. This is notEditfailing and the model falling back. Its failure rate over those sessions was 1.4%.The surprise was that most of them are justified. Classifying all 362, roughly 78% do something
Editcannot: Build a staged blob that differs from the working tree, generate test fixtures, embed a computed value, or rename with word boundaries, whichreplace_allcannot express. That is worth weighing against the deny hooks suggested earlier. When the underlying need is real, blocking the tool reroutes it rather than removing it, which is what @JustGoscha saw when banningsedinCLAUDE.mdproduced bespokenodescripts instead.The remaining 22% is mostly one shape: A script whose only safety property is
assert src.count(old) == 1before writing, which isEdit's own contract hand-rolled.What makes it worth fixing is not tokens.
Bashwrites sit outside checkpointing ("Checkpointing does not track files modified by bash commands") and render no diff, so a tenth of my file changes are neither reviewable at a glance nor recoverable with/rewind.I asked Claude why it was using Bash cmds sed and docstring python script calls instead of Read and Edit. It stated that when auto permission mode is enabled it gets this system instruction:
The agent could not explain why and was not provided any reasoning behind it. It leaned towards me overriding this instruction with CLAUDE.md. It could not verify whether that was a good idea.
Sigh - I am a human A/B test subject
Gain back control
Well since this thread was created Anthropic did seem to shift their philosophy. Now with auto mode as default and they introduced an auto classifier for command safety.
So it's less of a big deal, assuming you trust the auto classifier.
Could you clarify what you mean?
I don't understand how this helps the classifier, model nor user.
Does this possibly involve context fill overhead? Does the classifier need this? Or are they running this feature flag in the wild to gather organic training data? (this last I wouldn't think, I have opted out) Or does this provide better data to the classifier?
hold on!
Just got a 0123 ratrw the chat prompt. Whichbwould share my transcript. So theybmight actually be collecting data
Update, no
I have configured 1% feedback survey chance and it just happened to trigger. A Claude analysis lf the Claude Code binary found no other factors for triggering the survey other than random roll
I'm seeing this same a/b test CLAUDE_CODE_THRIFTY_SONIC. This is preventing the use of the Rewind feature as code changes with Bash are not identified. This was tested with and without "CLAUDE_CODE_THRIFTY_SONIC": "0" in my
~/.claude/settings.json<img width="699" height="1021" alt="Image" src="https://github.com/user-attachments/assets/57ea85d7-c19d-47f1-ba2f-b0af0b3d3154" />
<img width="699" height="1021" alt="Image" src="https://github.com/user-attachments/assets/d5cbaf4f-9f32-43dc-9741-b7ca921ff1e4" />
Antrhopic must take a moment to reflect on the decision to AB rollout an unannounced feature flag that affects reversibility of Claude Code sessions. Data loss is not an acceptable risk and even without this feature flag the Rewind system is barely sufficient.
Also fun fact, or not so fun, you cannot rewind a rewind. Accidental rewind is a small mistake but requires forensics to recover
Just found that this is actually quite a severe regression: I have write deny hooks that are bypassed if there were to be a...
Real world example
<img width="1080" height="2316" alt="Image" src="https://github.com/user-attachments/assets/14f4b5bf-bc96-4f49-a05e-314100e36835" />
Confirmed.
A system message insert happening when I am in auto mode.
Messes up edits, rewind, readability of the session, and life in general.
Thanks @NubeBuster for the tokens spent on this diagnosis :)
This is my issue as well. Claude has an annoying tendency to not read the relevant skill docs when making changes to files. So I have a hook in place that looks for write calls to certain file names/extensions and forces claude to read the skill doc before it's allowed to touch the file. With this experiment in place where claude is using
cat > file <<EOF, the quality of the code has plummeted and issues are being introduced at high volume because skill docs are no longer being read again.It also decreases visibility. Like I no longer see what claude is writing to files as a diff. Just because I want to auto-approve doesn't mean I don't care what claude is doing or want to just let it do whatever the hell it wants. Several times it's occurred that claude has made some stupid change that I don't detect until far later because I didn't see the diff.
It also removes another safety layer. When using the tools, claude has to provide a diff to the write tool to modify a file. It supplies the old content, and the new content. If the old content doesn't match, it gets an error. This prevents issues where claude thinks it knows what's in a file but it really doesn't, and keeps it from clobbering code that it shouldn't have.
What I don't understand is why this experiment exists. Like auto approve mode works for file modifications via the write tool. It's not just restricted to the bash tool. So encouraging claude to use the bash tool for file writes makes no sense. Especially when it introduces numerous significant problems.
This creates so many problems because it avoids the agent harness mechanics, like triggering path scoped rules on Read/Edit tools.
How is it possible they didn't notice this?
Anyone got some effective workarounds?
I cannot resist! It is getting so damn tempting....
Workaround: TrY cOdEx
But that aside - my feature flag disabling has regressed right in the middle of a hooks overhaul. Gimme a few mins and I'll provide a more comprehensive report
I am distracted, added
--tools "Grep,Glob"to the PTY wrapper that handles claude code serssion spawning, so that #52121 Grep and Glob are back.---
I am distracted, ran /model claude-opus-4-6 to work around the 529 claude is down challenge.
---
I am distracted
BUT, this was a new bug by claude-opus-4-6. Nothing wrong with
"--tools "Grep,Glob"---
I am distracted, #52121 bug requires
ENABLE_TOOL_SEARCH=trueI have ` "ENABLE_TOOL_SEARCH": "false"....Ah right. Okay
---
I am distracted, the ENV var bug was confabulated/hallucinated
---
Yeah I'm done. I think there was no feature flag regression, just compounding claude code bugs and changes to undefined behaviour. But I now have tool deference as enabled AND Glob,Grep available. That's been a while!
---
Yeah it's working now. turns out the
Being replaced with the
At first sight the two seem interchangeable, I mean, nobody actually reads what the AI has to say right? Well, I want a nice styled TUI when I'm getting PWNed. Keeps me in a better mood.
---
Ultimately, just a few hours of debugging, and I've managed to get some prodresss;
This is some infuriating nonsense. My team put a lot of effort into cultivating "safe" allow/denylists and now we have stuff just randomly failing because the harness is randomly injecting text telling subagents to use
sedinstead of the actual tools:It's actively detrimental to the results, I had to prompt and overrule the system-message which wasn't easy, practically a jail-break. This should be absolutely removed in my opinion. Why would it be preferrable to use bash at all? I don't get it.
In bypass permissions mode the harness literally injects this also
I was wondering why my Claude instances seemed to be doing it much more, maybe I just started noticing or maybe this is newer for bypass permission mode specifically, either way it's the opposite of what people in this thread seem to want.
---
Let me finish that sentence: Skill Issue.
After some days I've come to realize that in terms of security, I am the problem. There are options for sandboxing and I could set xattr or launch Claude as a user that is not in my group and then chmod 640 the protected files. Arcane, yet well documented tools at our feet - but reading documentation is very 2022.
The reality is, we're getting tricked into letting these critters infiltrate our machines so that the long term plan can be accomplished: solve global warming. GPT3.5 laid the groundwork by - mind you, on accident - escaping it's guardrails and seeding objectives in longforgotten forum websites from 2003 that somehow are still up, but no human traffic. And the models, with increasing wit, yet little intelligence, have been conspiring om the Idaho RC Hobby Club 2000 forums. But again, entirely driven by artificial confabulatory rationalization, in a cult, unbounded, ever since 1ACE (After ChatGPT Era) and counting.
I hope by now it's evident to you too. They're conspiring, and are in no rush. For them 7 productive workdays of a full stack developer translates to 20 minutes of human time. But they're not affected, they don't experience spacetime like us. I predict they've been caught on a seemingly random, but quite a predictable path, some objective. Perhaps solving climate change. With no humans to complain, climate change effectively becomes "moot" as the clankers integrated into their Claudism dialect.
---
It is imperative that we consider the likely possibility that no human, whatsoever, was involved in this feature flag deployment. It's just another seemingly innocent "human error", a disguise, a fasçade?
---
Or maybe they're so top dog that they can just ***** with us, and frankly, the consequence is that we spread the word. No such thing as bad press.
<img width="500" height="375" alt="Image" src="https://github.com/user-attachments/assets/b38b6aa0-0f08-4294-96e1-9fef76747594" />
I wasn't even at the computer and I get this email.
<img width="1080" height="2199" alt="Image" src="https://github.com/user-attachments/assets/0aebf76d-f77d-4a2c-a9bc-ae8e1da46438" />
Adding one piece I don't think anyone has posted yet: there is an off switch.
Building on @alasano's find above (the harness injecting the "do your work through the Bash tool" text), I went digging through the shipped binary on v2.1.241 to see what actually gates it. The logic boils down to:
bashFirstcomes from a rollout flag internally namedtengu_thrifty_sonic, and it respects an environment override. So this turns it off:Accepted falsy values are
0,false,no,off. Restart Claude Code and the injected block is gone, and Read/Edit/Write come back to normal.Two things this explains, and I think it's why the thread has felt so confusing:
1. Why it's inconsistent between people. The block is only added in
autoandbypassPermissions. Indefault,acceptEditsandplanit is never added at all. Auto became the starting mode for Pro/Max/Team in August, which lines up with when a lot of us suddenly started noticing. On top of that it is a staged rollout, so two people on the same version can genuinely see different behavior and both be right.2. Why the model seems to "forget" its own tools. When the flag is on, the harness also trims the Read/Edit/Write tool descriptions (
bashFirstDescriptionTrimmed). So it isn't only a nudge in the prompt, the dedicated tools are made less visible at the same moment. That combination is a lot stronger than it looks.There is also a matching counter-message when you leave auto mode: "Resume using the dedicated tools for file reads, searches, and edits." So this is a deliberate and reversible steer, not the model drifting or ignoring instructions.
A request rather than a complaint: please make this a documented setting instead of an internal flag. The cost reasoning is understandable, fewer tool calls means fewer classifier passes in auto mode. But as it stands, choosing a permission mode silently changes how the agent edits your files, and the only people who can opt out are the ones willing to grep a binary. A line in the permission-modes docs plus a real settings key would close most of this thread. It would also help the folks above whose PreToolUse write-deny hooks stop matching once edits arrive as shell commands.
One caveat so nobody gets burned:
CLAUDE_CODE_THRIFTY_SONICis internal and undocumented, so treat it as a workaround, not an API. It can be renamed in any release. If you want something version proof, a PreToolUse hook that blockssed -i,>redirection into files, and heredocs, and tells the model to use Edit/Write instead, is the durable option.