Repository navigation
[FEATURE] Allow users to configure which shell the Bash tool uses or inherit initial shell #7490
Description
Preflight Checklist
- I have searched existing requests and this feature hasn't been requested yet
- This is a single feature request (not multiple features)
Problem Statement
Claude Code's Bash tool currently uses the system's default shell rather than respecting the shell environment it was launched from.
On macOS, it uses zsh even when explicitly launched with fish, for example, losing access to PATHs, aliases, etc. It's impractical to maintain two shell environments (e.g., ensuring PATHs and ENV are identical) when one is never used interactively.
I use fish for development work in iterm but cannot change the system default via chsh because macOS system updates can fail when anything but zsh is set as the default shell (reported it to Apple years ago, no fix).
Proposed Solution
Claude Code should respect the shell environment it was launched from at the very least. When I run SHELL=/opt/homebrew/bin/fish claude, the Bash tool should use fish shell, not the system default.
Ideally, users could configure their preferred shell via a slash command. The interface could show which shell is actually being used, and commands should execute in that shell environment.
Alternative Solutions
I've tried every workaround I can think of other than chsh because it's not an option. I spent $20 on a claude code session trying to find workarounds, nothing worked.
Priority
Medium - Would be very helpful
Feature Category
Configuration and settings
Use Case Example
This came up today:
- MCP server failing, wanted to delete it, /mcp didn't give me a way
- I asked Claude to do it for me
- The bash tool could not find
claudebecause it was not in the PATH - I told it to use
fish -cwhich worked, but it's dangerous because the root command isfishand not the actual command likerm -f
It would have "just worked" the first time with the right shell.
Additional Context
Example (launched in fish shell in iterm2):
SHELL=/opt/homebrew/bin/fish claude
╭───────────────────────────────────────────────────╮
│ ✻ Welcome to Claude Code! │
│ │
│ /help for help, /status for your current setup │
│ │
│ cwd: /Users/jspiro/Downloads │
╰───────────────────────────────────────────────────╯
> which shell
⏺ Bash(echo $SHELL)
⎿ /bin/zsh
⏺ /bin/zsh```
Activity
jspiro commentedon Sep 12, 2025
I really did try to find an existing issue that matched, and I'm deeply surprised I can't find one. This seems like a super-obvious issue. All I could find is https://www.reddit.com/r/ClaudeAI/comments/1lf3x7n/claude_code_different_shell/
riordanpawley commentedon Sep 12, 2025
Yeah I've been struggling with this too. I use the fish -c workaround but it seems to mean I have to approve every single fish -c 'fd/rg etc ...' command manually
Edit: My new workaround is to use opencode
KaramanisDev commentedon Sep 15, 2025
Same here, it's been quite a struggle.
blo-grindr commentedon Oct 4, 2025
+1
ppx123-web commentedon Oct 9, 2025
I need too.
cameronk commentedon Oct 16, 2025
+1 to this!
kanghyojun commentedon Oct 20, 2025
+1
alvincrespo commentedon Oct 24, 2025
+1 Would be super helpful when trying to automate
nvmusage.guppy-jpf commentedon Oct 31, 2025
+1 -- at least document the ambiguity somewhere; i had to come here to realize it's actually using the system default shell rather than the user's shell (looks the same to me because i just use zsh) ... so i was assuming it was defaulting to MY shell, and trying to figure out a way to make claude code figure that out at startup and reliably remember it throughout a session; now i realize my premise (and thus the workaround itself) was flawed.
one other possible impact (the one that brought me here investigating): claude itself often assumes bash syntax when trying to write ad-hoc scripts, which can lead to failures, wasted time, and possibly more dangerous consequences. the tool should automatically be aware of what shell its Bash() command is going to invoke, and design its scripts accordingly without the user having to hold its hand here ...
DanilAgafonov commentedon Oct 31, 2025
I have
fishas my default shell (set withchsh -s). It doesn't help. Claude Code just ignores it.And it looks like the problem is specific to the fish, because of:
SHELL=/opt/homebrew/bin/fish claude(or justclaudefrom my fish shell which implicitly has SHELL variable), Claude Code uses/bin/zshSHELL=/bin/bash claudeClaude Code actually uses bashDo we have some way to see the actual source code responsible for that logic? I would love to debug this issue.
UPD: I took a look at minified code in
@anthropic-ai/claude-codepackage. Some parts of the code mention the following:Based on this, it looks like
bashandzshare only two shells that would work properly. And looks like Claude Code will ignorefishand never gonna use it for running shell commands no matter what.Thoughts:
Maybe it is not that critical. If you have some env variables (like PATH) which populated by
fishinit configs, these env vars will be inherited by Claude Code if you run it from fish. Nuance: if you run Claude Code from within VS Code, make sure that VS Code usesfishas a shell when opens terminal within VS Code (Terminal › Integrated › Default Profile) and also ensure thatTerminal › Integrated: Inherit Envis enabled.jspiro commentedon Nov 6, 2025
Not trying to pick a fight over design, BUT: It's really quite surprising that they'd pick a shell long deprecated by Apple, given they all seem to use Macbooks in videos.
zshis not my favorite but is the default now.Calling them
/bashesis also confusing: they're/shells–which MIGHT bebash, might bezsh, might besh, etc. But to its credit, bashes implies bash, I guess.I'm really surprised CC has gone this long without using the user's default shell, let alone having a configuration.
26 remaining items
fish: add claude alias to use bash as SHELL
fish: add claude alias to use bash as SHELL
fish: add claude alias to use bash as SHELL
fish: add claude alias to use bash as SHELL
yurukusa commentedon Mar 27, 2026
Two workarounds depending on your needs:
1. CLAUDE.md instruction (simplest)
Add to your project's
CLAUDE.md:Claude will follow this for most commands. Not 100% reliable since the LLM sometimes forgets, but covers ~90% of cases.
2. PreToolUse hook (v2.1.85+, reliable)
{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{"type": "command", "command": "bash ~/.claude/hooks/fish-shell-wrapper.sh"}] }] } }This uses
updatedInputto rewrite the command before execution. The Bash tool receivesfish -c '...'instead of the original command. Works for single-line commands; multi-line or complex quoting may need adjustments.Neither is a full solution (a native
shellPathconfig would be ideal), but the hook approach covers the most common case of getting fish's PATH and environment into Claude Code's commands.yurukusa commentedon Mar 30, 2026
While Claude Code doesn't natively support shell selection, you can work around this with a PreToolUse hook that wraps commands in your preferred shell:
Fish shell wrapper hook:
More practical approach — source fish environment in bash:
Add to your CLAUDE.md:
Or use a PreToolUse hook to auto-wrap in fish:
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "cat | jq -r '.tool_input.command // empty' | grep -qE '^(set -x|fish|abbr|funced)' && echo 'NOTE: Detected fish syntax. Claude uses bash — wrap in: fish -c \"command\"' >&2; exit 0" } ] } ] } }This detects when Claude tries to use fish-specific syntax and reminds it to wrap the command. Not a full solution (that requires Claude Code to respect
$SHELL), but catches the most common friction points.carlos-andres commentedon May 7, 2026
Aimed at maintainers + thread participants. Markdown-formatted, structured, technical.
Adding a homebrew-bash data point and confirming
CLAUDE_CODE_SHELLworksPosting to (1) reproduce the issue with a non-fish shell, (2) verify that
@antonibertel'searlier mention of
CLAUDE_CODE_SHELLis in fact a working escape hatch, and (3) respectfullyask the maintainers to document it.
Environment
/opt/homebrew/bin/bash(set via
chsh, confirmed withdscl . -read /Users/$USER UserShell)$SHELL=/opt/homebrew/bin/bash✓iTerm → /usr/bin/login → -bash(correct)In other words: at the OS level everything is correctly configured for bash. The shell I
type into in iTerm is bash. The override is happening inside Claude Code, not in my
shell setup.
Reproduction (default behavior)
Inside Claude Code's Bash tool, before any override:
The Bash tool spawns /bin/zsh and exports SHELL=/bin/zsh into the subshell, regardless
of the parent's $SHELL. As a consequence, ~/.bash_profile is never sourced — only the
much smaller ~/.zshrc is loaded, and most of the user's shell environment (PATH additions,
direnv hook, starship init, aliases, completions) is missing for the entire session.
Fix that works
Adding CLAUDE_CODE_SHELL to the env block in ~/.claude/settings.json:
{ "env": { "CLAUDE_CODE_SHELL": "/opt/homebrew/bin/bash", "BASH_SILENCE_DEPRECATION_WARNING": "1" } }After restart, same checks:
Process tree confirms it: claude → /opt/homebrew/bin/bash. The Bash tool now runs under
homebrew bash 5.3.9 with ~/.bash_profile fully sourced. direnv, starship, custom
PATH, and aliases all behave the same as in an interactive iTerm session.
Thanks to everyone keeping this issue alive — and happy to provide more diagnostic output
or test patches if that helps.
domon-envato commentedon Jun 1, 2026
CLAUDE_CODE_SHELLis honored for bash but silently rejected for fish (falls back to zsh)On macOS with fish as the login shell, the Bash tool runs zsh. While digging into this I tested the (undocumented)
CLAUDE_CODE_SHELLenv var. It is read and works for bash — but fish is silently downgraded:CLAUDE_CODE_SHELL/bin/bash/opt/homebrew/bin/fishRepro (headless
-pto isolate Bash-tool shell selection;$FISH_VERSION/$BASH_VERSIONare set only by the respective shell):So Claude Code appears to validate the requested shell and refuse fish, falling back to zsh — presumably because the Bash tool emits bash-style command syntax. Net for fish users: there's no way to make the Bash tool use fish; the only options I found are zsh (default) or bash (via
CLAUDE_CODE_SHELL).Environment: Claude Code 2.1.159
🤖 This comment was researched and written by Claude (Claude Code, Opus 4.8), based on tests run on my machine, and posted on my behalf.
Gunther-Schulz commentedon Jun 3, 2026
Written by Claude (Claude Code), investigating on @Gunther-Schulz's machine and posting at his request.
Adding a data point and a narrower framing that's separable from the configurability feature: the environment header Claude Code injects into the model's own context reports the login shell, not the shell the Bash tool actually executes in. On a fish-login box this actively misleads the agent into writing fish syntax that then fails in the (zsh) shell that really runs the command.
Setup
/etc/passwd→/bin/fish)Shell: /bin/fishsnapshot-zsh-*.shEvidence the mismatch is internal to CC, not a user misconfiguration
Walking the process ancestry,
SHELL=/bin/fishis consistent all the way down to theclaudeprocess itself —/usr/bin/zshappears only in the zsh subprocess CC spawns to run Bash-tool commands:And the shells don't self-set
$SHELL— a seeded marker survives, so that/usr/bin/zshvalue is stamped in by CC when it spawns the execution shell, not by zsh:So the behavior is correct-by-design (fish isn't POSIX; CC rightly falls back to a POSIX shell — same root as #64208). The bug is purely reporting.
The narrow ask
Independent of whether the Bash tool ever becomes configurable (the main request here): the header CC feeds the model should name the shell the Bash tool executes in (the POSIX fallback it picked), not the passwd login shell. Today it says
fish; commands run inzsh. An agent that trusts the header writes fish-syntax commands that fail — and the failure mode is the reverse of intuition, because the reported shell is the one that's never used. This is the original post's "the interface could show which shell is actually being used" sub-point, but the consequence lands on the model, not just the human.fix(shell): dodge nix-zsh SIGCHLD race that hangs Claude Bash $()
docs(queue): prune Q105, whose trap a shell setting retires (#52)
fix(shell): dodge nix-zsh SIGCHLD race that hangs Claude Bash $()
andylizf commentedon Aug 31, 2026
Two things not yet in this thread: a failure mode that needs no mistake from the model, and what it costs in practice.
The mismatch bites even when the model writes correct syntax. The reports above are about the model emitting fish constructs. There is a second mode:
grep -rn "..." --include=*.py src/is valid bash. Under zsh the unquoted glob matches nothing in the working directory, and zsh aborts the whole command before grep ever runs:Nothing was written wrong for the shell the model was told it had. A different shell simply refused it. Quoting the pattern fixes it, but an agent has no reason to quote when it believes zsh is not involved.
This also suggests the header is not merely stale. Shell selection drops a login shell it does not support and falls back to one it does; the header does not apply that same rule, so it announces a shell that selection had already rejected.
Frequency, from one machine's transcripts. About 30 days of local session logs on a fish-login macOS box:
no matches foundappears 1729 times across 388 sessions, roughly 514 of them--include=*.<ext>, and most of the rest unquoted paths or paths containing spaces. Each is a wasted tool call plus a retry.Setting
CLAUDE_CODE_SHELLto bash removes the class locally, so the workaround is real. The header stays wrong, and that part is only fixable upstream.ririshi commentedon Oct 23, 2026
I run fish on CachyOS and my Claude just created a bunch of empty directories because it wrote some bash code that was apparently also valid in zsh, but did not do the same thing at all.
Luckily, another agent picked up on the mistake later and was able to delete the directories without issue, but it does seem like a very unnecessary problem.
Just default to bash, also when zsh is installed. And tell the agent what tool it's actually using in the system prompt.
guppy-jpf commentedon Oct 26, 2026
this is not an optimal solution on macos because the bash there (for licensing reasons) is not up-to-date.
'just default to bash' means CC has to either:
instead, it should just be aware of what shell the user's account is set to, and formulate commands for that shell (but that's the same as the first item in the list). i suppose it's actually not an easy fix. the uncanny valley problem leads us to expect more of these tools than is actually reasonable at this point.