[BUG] Newlines stripped in for loops with pipes
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?
Multi-line for loops that contain pipe characters in the loop body fail with "unexpected end of file" syntax errors. The newlines are being incorrectly removed when Claude Code processes these commands, causing bash to see an incomplete loop structure.
What Should Happen?
Ideally, Claude should execute the command that it actually displays when it asks for permission to run it; or, failing that, it should replace newlines with semicolons to keep the syntax intact.
Error Messages/Logs
/opt/homebrew/bin/bash: eval: line 2: syntax error:
unexpected end of file from `for' command on line 1
Steps to Reproduce
for i in a b c; do
echo "test" | cat
done
Claude Model
Sonnet (default)
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.0.35 (Claude Code)
Platform
AWS Bedrock
Operating System
macOS
Terminal/Shell
Terminal.app (macOS)
Additional Information
When Claude Code transforms the command for execution via eval, it appears to be stripping newlines or improperly joining lines when a pipe character is present. This causes bash to interpret the multi-line loop as a single incomplete line. I have occasionally persuaded Claude to add semicolons where the newlines were to fix the syntax, but it seems impossible to get this fix to stick. (Perhaps note that while semicolon _generally_ is equivalent to newline, there should be no semicolon after do or then or possibly a few other keywords.)
This seems vaguely related to #7387, #10350, #10153 etc but I have not found a report of this specific error.
6 Comments
PreToolUse Hook Workaround for Bash Preprocessing Bugs
Created a hook that fixes all 4 known bash preprocessing bugs in Claude Code by wrapping problematic commands in
bash -c '...'.ZERO TOKEN OVERHEAD - hooks run externally before command execution, no context consumed.
---
GitHub Issues Fixed
| Issue | Problem | Link |
|:------|:--------|:-----|
| #11225 |
$(...)command substitution mangled | View || #11182 | Multi-line commands have newlines stripped | View |
| #8318 | Loop variables silently cleared with pipes | View |
| #10014 | For-loop variable expansion issues | View |
---
How It Works
The hook intercepts Bash tool calls before execution and:
$(...)command substitution outside single quotes\n)for ... | ...or| while ...)bash -c '...'to bypass preprocessing:'→'\'')<<) - skips continuation fixing (heredocs handle newlines correctly)if/then,for/do,while/do,case/in) - skips continuation fixing (newlines are intentional statement separators)---
Test Results
| Test Suite | Tests | Pass |
|:-----------|------:|:----:|
| Pattern detection tests | 139 | 100% |
| Execution tests | 45 | 100% |
| Edge case tests | 29 | 100% |
| Adversarial tests | 60 | 100% |
| JSON format tests | 26 | 100% |
| Total | 299 | 100% |
Includes regression tests for:
for→if→if)for→while→if)---
Installation
Step 1: Save the hook
Step 2: Make executable
Step 3: Configure Claude Code
In Claude Code, run:
Then:
~/.claude/hooks/fix-bash-substitution.pyStep 4: Restart session
Start a new Claude Code session for the hook to take effect.
---
Verifying It Works
Test with a command that would normally fail:
If the hook is working, you'll see commands wrapped in
bash -c '...'in the execution output.---
Troubleshooting
Hook not running?
ls -la ~/.claude/hooks/fix-bash-substitution.py/hooksin Claude CodeStill getting errors?
echo '{"tool_name":"Bash","tool_input":{"command":"echo $(date)"}}' | python3 ~/.claude/hooks/fix-bash-substitution.py---
Changelog
| Version | Date | Changes |
|:--------|:-----|:--------|
| v5 | 2025-12-10 | Skip continuation-fixing for control structures (fixes nested if/for/while) |
| v4 | 2025-12-10 | Heredoc detection - skip continuation fixing for
<<|| v3 | 2025-12-10 | Quote-aware continuation fixing |
| v2 | 2025-12-09 | Added loop-with-pipe detection |
| v1 | 2025-12-09 | Initial release - command substitution fix |
This issue has been inactive for 30 days. If the issue is still occurring, please comment to let us know. Otherwise, this issue will be automatically closed in 30 days for housekeeping purposes.
Yes, this is very much occurring still. It is a daily time and token waster.
I'm tracking a related issue in #11225 and retested this specific reproduction on v2.1.25 — it appears to be fixed now.
Output:
The multi-line for loop with pipe executes correctly without syntax errors.
Are you still seeing this on the latest version? I'd like to know if I can stop tracking this particular pattern in my issue.
Note: A related bug with
$VARexpansion being stripped when pipes are present still exists (#11225, #8318), but that's a different symptom than the newline/syntax error you reported.I concur; this appears to be fixed now.
This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.