[BUG] Bash tool drops pipe stdin when command contains command substitution syntax (backticks or $(...))
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?
The Bash tool silently drops pipe stdin when the command string contains command substitution syntax — either backticks (cmd) or $(cmd).
The left side of the pipe produces no output, so the right side receives empty stdin.
This affects real-world usage when piping content containing $(...) or backticks to commands. For example, piping a git commit message with heredoc syntax through a script:
echo 'git commit -m "$(cat <<'"'"'EOF'"'"'
commit message here
EOF
)"' | some_script bash
The eval '...' < /dev/null wrapper that the Bash tool uses appears to expand command substitution syntax inside the command string before the pipe runs, causing the < /dev/null to feed into the substituted command instead of being inert.
Pipe stdin should be preserved regardless of what characters appear in the command string. Single-quoted content should never be subject to command substitution expansion.
Bash(echo 'A$(B)C' | cat)
(No content)
What Should Happen?
Pipe stdin should be preserved regardless of what characters appear in the command string. Single-quoted content should never be subject to command substitution expansion.
Bash(echo 'A$(B)C' | cat) # should output A$(B)C.
Error Messages/Logs
No error messages. The failure is silent — the tool returns empty output with exit code 0.
Steps to Reproduce
- Create a test script that dumps its stdin (/tmp/test_stdin.py):
#!/usr/bin/env python3
import sys, json
raw = sys.stdin.read() if not sys.stdin.isatty() else None
print(json.dumps({"stdin": raw, "stdin_bytes": len(raw) if raw else 0}))
- Ask Claude Code to run these Bash commands:
# FAILS — empty output, stdin lost:
echo 'A$(B)C' | cat
echo 'A`B`C' | cat
echo 'commit -m "$(cat <<EOF
msg
EOF
)"' | python3 /tmp/test_stdin.py bash
# WORKS — adding ; true makes it work (different eval code path?):
echo 'A$(B)C' | cat; true
echo 'A`B`C' | cat; true
- Observe that commands with command substitution syntax produce empty output, but the identical commands with ; true appended work
correctly.
Systematic test results from within a Claude Code session:
┌───────────────────────────┬───────────┐
│ Command │ Output │
├───────────────────────────┼───────────┤
│ echo 'hello' | cat │ hello ✓ │
├───────────────────────────┼───────────┤
│ echo 'A&&B|C' | cat │ A&&B|C ✓ │
├───────────────────────────┼───────────┤
│ echo 'A$B' | cat │ A$B ✓ │
├───────────────────────────┼───────────┤
│ echo 'A${B}C' | cat │ A${B}C ✓ │
├───────────────────────────┼───────────┤
│ echo 'A\B`C' | cat` │ (empty) ✗ │
├───────────────────────────┼───────────┤
│ echo 'A$(B)C' | cat │ (empty) ✗ │
├───────────────────────────┼───────────┤
│ echo 'A$(' | cat │ (empty) ✗ │
├───────────────────────────┼───────────┤
│ echo 'A\B`C' | cat; true` │ A\B`C` ✓ │
├───────────────────────────┼───────────┤
│ echo 'A$(B)C' | cat; true │ A$(B)C ✓ │
└───────────────────────────┴───────────┘
Claude Model
Opus
Is this a regression?
I don't know
Last Working Version
_No response_
Claude Code Version
2.1.32
Platform
Anthropic API
Operating System
Other Linux
Terminal/Shell
Other
Additional Information
Related issues sharing the same eval '...' < /dev/null root cause:
- #1132 — < /dev/null injected into middle of piped jq commands
- #2383 — zsh pipes break with nomultios due to trailing < /dev/null
- #2851 — < character causes /dev/null injection
- #7387 — $(...) gets mangled to \$ ( ... ) with /dev/null injection
- #15599 — multi-line + pipe commands with $() corrupted
The common root cause across all these issues is the eval '...' < /dev/null wrapper. The ; true workaround suggesting there are two code paths — the simpler one (no ;) handles the eval wrapping differently and breaks on command substitution syntax.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗