Bash tool: "wc" via command substitution fails with "command not found" inside a for-loop with 2+ items
Description
Calling wc via command substitution ($(... | wc -l)) inside a for loop with 2 or more items fails with command not found: wc, on the very first iteration — not something that appears only after repeated calls.
grep -c "" in the exact same shape works fine, so this isn't a generic loop issue — it's specific to wc.
Minimal reproduction
for n in 1 2; do y=$(echo "test" | wc -l); echo "$y"; done
Expected: prints 1 twice.
Actual:
(eval):1: command not found: wc
1:
(eval):1: command not found: wc
2:
A single-item loop (or no loop at all) with the identical command works correctly:
for n in 1; do y=$(echo "test" | wc -l); echo "$y"; done
# → 1: 1 (works)
type wc resolves correctly to /usr/bin/wc both inside and outside the loop.
What I ruled out
- A third-party PreToolUse hook (RTK,
rtk-ai/rtk) — directly simulatedrtk hook claudeagainst the exact failing command via its documented stdin JSON contract; it produced no rewrite at all (empty stdout, exit 0), both for this minimal case and for the original real-world case that surfaced it (afind | wc -lcount inside a repo-survey loop). - The
findshell function that Claude Code injects into shell snapshots (find() { ... exec -a bfs "$_cc_bin" ... }, shelling out to the Claude binary itself forbfs-style semantics) — bypassing it withcommand finddid not fix anything, andfindalone across 2 iterations works fine on its own, so it's unrelated. - Any
wc-specific entry in my own Bash permission settings (allow/denylists) — none exist.
This points at something in the Bash tool's own sandboxing/permission-parsing layer mishandling wc specifically when it appears inside a multi-iteration loop body, but I don't have visibility into that layer to go further.
Environment
- Claude Code version: 2.1.227 (from the running process path; a separately-installed
claudeCLI on PATH reports a different, older 2.1.112 — worth noting in case that's relevant) - OS: macOS 26.6 (build 25G72), arm64
- Shell: zsh 5.9 (
/bin/zsh)
Impact
Silent, misleading failure — the loop continues (doesn't crash), so the wrong/empty result can go unnoticed rather than erroring loudly. Workaround is straightforward (grep -c "" instead of wc -l, or shell out to Python for counting inside loops), but the underlying behavior is surprising and cost real debugging time to trace.
3 Comments
Could not reproduce on v2.1.233 (macOS, arm64, zsh 5.9).
Steps tried:
claudein a fresh empty project (alsoclaude -p, and with the Bash sandbox enabled via"sandbox": {"enabled": true}in.claude/settings.json).for n in 1 2; do y=$(echo "test" | wc -l); echo "$n: $y"; donefind ... | wc -lshape:for d in . ..; do c=$(find "$d" -maxdepth 1 -type f | wc -l); echo "$d: $c"; doneObserved (every variant):
Expected: the same, so no failure seen.
wcresolved fine on both iterations, in interactive mode,-pmode, and with the sandbox on.Assessment: If this happens, it's a genuine bug (
wcshould work anywhere in a Bash tool command), but Claude Code doesn't treatwcspecially, and it doesn't rewrite loop bodies. The(eval):1:prefix on the error is normal for every Bash tool command and isn't a clue on its own. Because Claude Code replays your zsh startup files (functions, aliases, options, PATH) into the tool shell, this looks environment-specific. To narrow it down, could you:type -a wc; alias wc; which -a wcin a normal terminal and paste the output.claudewith a clean shell config (ZDOTDIRpointing at an empty directory) and check whether the loop works./sharelink from a session where it fails.🤖 Generated with Claude Code
We weren't able to reproduce this. Could you provide steps to trigger the issue — what you ran, what happened, and what you expected? This issue will be closed automatically if there's no activity within 7 days.
Thanks for the detailed report and the minimal repro. I tried to reproduce this on 2.1.233 (Linux, zsh 5.9, default settings, and also on 2.1.229 to match your version): the exact command
for n in 1 2; do y=$(echo "test" | wc -l); echo "$n: $y"; doneprints1: 1/2: 1correctly,type wcresolves normally, and there's nowcshell function inside the Bash tool. I couldn't exercise the macOS sandbox path from this environment, and that's the leading suspect here — the(eval):1:error prefix matches how sandboxed commands run.To pin this down, could you share:
/sandboxor"sandbox": {"enabled": true}in settings)? If so, does the loop work with sandboxing temporarily disabled?for n in 1 2; do y=$(echo test | command wc -l); echo $y; doneclaude update, currently 2.1.233), plus your macOS version./sharelink from a session that hits the error.Not reproducible on Linux so far, so this looks macOS-specific — likely in the sandboxed execution path rather than
wcitself.🤖 Generated with Claude Code