defaultShellでshell modeの既定シェルを変更する
settings.jsonのdefaultShellは!から始まる対話コマンドの既定シェルを切り替える設定です。効かないときの切り分けと、hooks・Skillsの同名設定との違いを扱います。
settings.jsonのdefaultShellは、入力欄で!から始めて実行する対話コマンドの既定シェルを切り替える設定です。値は"bash"か"powershell"で、WindowsでBashが使えない環境では自動的に"powershell"になります。
名前だけ見るとClaude Code全体の既定シェルを決める設定に見えますが、切り替わるのは!コマンドだけです。Claudeが呼ぶBashツールも、hooksやSkillsのシェルも別の設定で決まります。「"powershell"にしたのにbashで動く」という症状は、この範囲の違いか、PowerShellツールの有効化漏れで説明がつくことがあります。
!コマンドが意図と違うシェルで動くときの切り分け
Claude Codeの入力欄で!を先頭に付けたコマンドは「shell mode」で実行されます。Claudeの承認を挟まず直接シェルへ渡され、出力は会話に追加されます。
! npm test
! git statusshell modeから抜けるには、空の入力欄でEscape・Backspace・Ctrl+Uのいずれかを押します。!で始まるテキストを空の入力欄に貼り付けた場合も、自動でshell modeに入ります。実行が長引くコマンドはCtrl+Bでバックグラウンドに回せます。v2.1.186以降は、コマンドの出力が会話に載るとClaudeがそのまま応答します。応答させたくないときはrespondToBashCommandsをfalseにします。
意図したシェルで動かないときは、次の順で確認すると原因を絞れます。
defaultShellが効かないときの確認順
- 1
設定ファイルの値を見る
defaultShellは"bash"か"powershell"の文字列です。ユーザー設定・プロジェクト設定・ローカル設定のどのファイルにも置けるので、別のファイルが上書きしていないかも見ます。ファイルごとの優先順位はClaude Code settings.json完全ガイドにあります。 - 2
PowerShellツールが有効か確かめる
"powershell"はPowerShellツールが有効な間だけ機能します。Windows(Git Bashなし)では自動で有効、Windows(Git Bashあり)ではclaude.aiとConsoleのアカウントで既定で有効です。Linux・macOS・WSLではCLAUDE_CODE_USE_POWERSHELL_TOOL=1が要ります。Bedrock、Google CloudのAgent Platform、Microsoft Foundryのセッションも同じです。 - 3
無効なら反対側のシェルに回る
指定したシェルが使えないとき、Claude Codeはもう一方を使います。PowerShellツールが無効なら
"powershell"はBashに、Bashが入っていなければ"bash"はPowerShellに回ります。エラーにならないので、設定が無視されたように見えます。 - 4
Claude Codeをどこで動かしているかを見る
デスクトップアプリのCodeタブでは、CLIと挙動が違うという報告があります。GitHubのissue #93662(2026-09-11起票、open)は、Windowsのデスクトップアプリの報告です。
"defaultShell": "bash"が無視され、!コマンドが常にWindows PowerShell 5.1で動くとあります。CLIでは効いているという前提の報告です。
PowerShellツールの有効化は、Linux・macOS・WSLなら次の設定になります。pwsh(PowerShell 7以降)がPATH上に必要です。ほかの環境変数はClaude Code環境変数リファレンスにまとまっています。
{
"env": {
"CLAUDE_CODE_USE_POWERSHELL_TOOL": "1"
},
"defaultShell": "powershell"
}有効にすると、ClaudeはPowerShellを主シェルとして扱います。Git Bashが入っているWindowsでは、POSIXスクリプト用にBashツールも使えるままです。
実行ポリシーは、プロセス単位で-ExecutionPolicy Bypassを付けて起動されます。グループポリシーのMachinePolicyとUserPolicyは上書きされません。マシンの実効ポリシーに従わせたいときはCLAUDE_CODE_POWERSHELL_RESPECT_EXECUTION_POLICY=1を設定します。bashとzshの起動ファイルが読まれる条件はClaude Codeのシェル起動設定にあります。
defaultShellはBashツールを置き換えない
defaultShellを"powershell"に変えても、Claudeがツール呼び出しで実行するコマンドの経路は変わりません。GitHubのissue #98901(2026-10-02起票、open)の報告です。issue本文では、Windows 11で"defaultShell": "powershell"にしても、Bashツールはbash.exeを探しにいきます。Git Bashのbash.exeが見つからず、失敗したとあります。対象はClaude Code v2.1.280で、issueはリグレッションだとしています。設定の説明どおりに読めば、この設定が対象にするのは!コマンドだけです。
Bashツールの代わりにPowerShellツールを主役にしたいなら、触るのはdefaultShellではありません。PowerShellツールの有効化(CLAUDE_CODE_USE_POWERSHELL_TOOL)の側です。
もう1つ、shell modeの!コマンドは、サンドボックスを有効にしていても原則サンドボックスの外で動きます。サンドボックスが対象にするのはClaudeが実行するコマンドで、利用者が自分で打つコマンドは含まれません。例外は2つです。バックグラウンドセッションと、CLAUDE_CODE_SUBPROCESS_ENV_SCRUBを設定したLinuxセッションでは、!コマンドもサンドボックスの中で動きます。
3つのシェル設定は効く範囲が違う
シェル選択に関わる設定は、PowerShell向けに3つあります。同じshell系の名前でも、有効化の条件が揃っていません。
PowerShellを使う3つの設定
defaultShell
settings.jsonの設定で、!から始まる対話コマンドが対象です。PowerShellツールの有効化が必要です。hookの shell
個別のcommand hookに書くフィールドで、そのhookの実行だけが対象です。hookはPowerShellを直接起動するため、
CLAUDE_CODE_USE_POWERSHELL_TOOLの値に関係なく動きます。Skillの shell: powershell
Skillのfrontmatterに書き、中の
!`command`ブロックが対象です。PowerShellツールの有効化が必要です。
hookのshellは、省略するとWindowsでGit Bashが無いときだけPowerShellになります。defaultShellを変えてもhookは切り替わらないので、hookをPowerShellで動かすなら個別に指定します。hookの書き方はClaude Code Hooksの設定方法にあります。Windowsではpwsh.exe(7以降)を自動検出し、見つからなければpowershell.exe(5.1)を使います。
Skillには別の失敗の仕方があります。frontmatterでshell: bashと書いたSkillをGit Bashの無いWindowsで呼ぶと、コマンドが走る前に失敗します。表示はSkill <name> requires bash (`shell: bash` in frontmatter) but Git Bash was not foundです。
PreToolUseフックでシェルコマンドを検査している場合は、BashだけでなくBash|PowerShellにマッチさせます。PowerShellツール経由のコマンドはBashのマッチャーに掛からないためです。
{
"type": "command",
"shell": "powershell",
"command": "& \"$env:CLAUDE_PROJECT_DIR\\.claude\\hooks\\check.ps1\""
}この例はプロジェクトルートの参照に$env:CLAUDE_PROJECT_DIRを使っています。PowerShellのシェル形式コマンドでは、${CLAUDE_PROJECT_DIR}は${env:CLAUDE_PROJECT_DIR}の形に書き換えられます。この展開はダブルクォートの中でしか効きません。シングルクォートの中では、PowerShellが変数を展開しないためです。
裸の$CLAUDE_PROJECT_DIRは書き換えの対象外です。PowerShellが未定義のローカル変数として$nullに解決するので、スクリプトパスからプロジェクトルートの接頭辞が消えます。Claude Codeはデバッグログに警告を残すだけなので、気づきにくい失敗です。
WSL2とPowerShellが混在する環境での設定
WSL2からWindows側のPowerShellも併用する構成では、次の組み合わせになります。
| シーン | 設定 |
|---|---|
WSL2内でbashのまま、!コマンドもbash | 設定defaultShellを設定しない(既定の"bash") |
WSL2内で!コマンドだけPowerShell | 設定CLAUDE_CODE_USE_POWERSHELL_TOOL=1とdefaultShell: "powershell" |
| WindowsネイティブでGit Bashなし | 設定設定不要(自動でPowerShellが既定になる) |
Windowsネイティブで、hookだけPowerShell、!コマンドはbash | 設定defaultShellは触らず、対象hookに"shell": "powershell" |
WSLでPowerShellツールを使うには、WSL側のPATHにpwshが必要です。Windows側にだけPowerShellがある状態では要件を満たしません。
PowerShellツールの癖と既知の制約
PowerShellツールには、プレビュー中の制約が2つあります。PowerShellプロファイル($PROFILE)を読み込まないことと、Windowsでサンドボックスに対応しないことです。プロファイルに書いたエイリアスや関数は、ツール経由のコマンドから参照できません。
Windowsでは、v2.1.214以降で文字コードと終了コードの扱いが変わっています。
- PowerShell 5.1の
>と>>がUTF-8で書き込む(それ以前はUTF-16LE) - 標準入力へのパイプがUTF-8でエンコードされる(それ以前は非ASCII文字が
?になる) - 標準入力を待つ子プロセスが、ハングせずEOFを受け取る
- エラー出力が、ANSIエスケープなしで取得される
終了コード1の扱いも変わっています。v2.1.196以降は、grep・rg・findstrなどの終了コード1は「一致なし」の意味で、失敗として報告されません。git diffの終了コード1は「差分あり」の意味です。robocopyは終了コード0〜7が情報で、8以上が失敗です。
where.exeの終了コード1は「一致なし」を意味します。fc.exeとdiff.exeの終了コード1は「ファイルが異なる」を意味します。ただし、この扱いは出力があるときに限ります。
where.exe /Qや$nullへのリダイレクトのように出力を消した形は、終了コード1でも失敗として報告されます。
まとめ
!コマンドの挙動を変えたいならdefaultShell、Claudeのツール呼び出しの経路を変えたいならPowerShellツールの有効化、と分けて考えると迷いません。