Claude Code 在 Windows 下强制走 PowerShell 的配置教程
场景:Windows + pwsh 7+ 的开发环境,想让 Claude Code(以下简称 CC)默认所有 shell 操作走 PowerShell Tool 而不是 Bash Tool。只改两个文件(CLAUDE.md + settings.json),附实测命中率 100% 和性能基准数据。
省流:一句 prompt 让 CC 自己配好
点开复制这段 prompt,粘进 CC 主会话它会把两处配置合并落地(保留现有内容不覆盖)
把下面两处配置落地到我本地,保留原有内容不要覆盖,合并而不是替换: 【1】在 ~/.claude/CLAUDE.md 顶部追加以下三段 markdown: ```markdown # Global PowerShell Rules (Windows + pwsh 7+) ## Shell Preference - **禁止调用 Bash Tool。** 需要执行 shell 命令时,只能使用 PowerShell Tool(pwsh.exe)。这是工具层的硬约束,不是命令风格偏好。 - 不要把 Unix 惯用语法(`ls | head`、`grep`、`curl`、`find`、`cat`)作为选 Bash Tool 的理由——改用对应的 PowerShell cmdlet(`Get-ChildItem | Select-Object -First N`、`Select-String`、`Invoke-WebRequest`、`Get-ChildItem -Recurse`、`Get-Content`)。 - 列文件用 Glob,读文件用 Read,搜内容用 Grep——dedicated tool 优先级仍然高于 shell。 - 只有用户明确说"用 bash"或"sh"时,才可以考虑 Bash Tool;即便如此也优先 PowerShell 等价写法。 - 路径一律使用 Windows 原生风格(C:\ 或 $env:USERPROFILE),避免 / 路径。 ## PowerShell 编码规范 - 函数/命令动词必须来自 Get-Verb(Get-、Set-、New-、Remove- 等)。 - 函数名/类名用 PascalCase,变量/参数用 camelCase。 - 优先使用 Write-Verbose / Write-Debug / Write-Information,**严禁随意使用 Write-Host**(除非必须格式化控制台输出)。 - 模块结构严格遵循:src/Public、src/Private、src/Classes + 静态 dot-source + Export-ModuleMember。 - 所有脚本必须加上 #Requires -Version 7.0。 - 错误处理统一使用 try/catch + ErrorAction Stop + Write-Error。 - 测试必须用 Pester,静态检查用 PSScriptAnalyzer。 ## 常用指令 - 每次生成代码后自动运行 PSScriptAnalyzer 检查。 - 优先使用原生 cmdlet(Get-Command、Invoke-WebRequest、ConvertFrom-Json 等),不要自己造轮子。 ``` 【2】把以下两项合并进 ~/.claude/settings.json(保留其他字段): ```json { "env": { "POWERSHELL_TELEMETRY_OPTOUT": "1" }, "defaultShell": "powershell" } ``` 【3】把以下工具合并进 ~/.claude/settings.json 的 permissions.allow 数组(保留已有其他元素): ```json ["PowerShell", "Read", "Write", "Edit", "MultiEdit", "Glob", "WebFetch", "WebSearch", "NotebookRead", "NotebookEdit"] ``` 【4】把 "Bash" 加入 ~/.claude/settings.json 的 permissions.deny 数组;如果 permissions.allow 里有 "Bash",一并移除(避免 allow/deny 同时包含造成歧义): ```json { "permissions": { "deny": ["Bash"] } } ``` 改完列出具体改动点。
想看清楚每一处改了什么再动手,继续往下读。
背景:CC 为什么默认会掉进 Bash
CC 在 Windows 上同时暴露 Bash 和 PowerShell 两个 shell 工具。没有明确指令时模型按任务直觉挑工具——自然语言里一旦出现 grep、curl、find、ls -la 这类 Unix 惯用词,就容易掉进 Bash 分支。带来的问题:
- 路径风格变成
/c/Users/...,跟 PowerShell 脚本不一致 Select-String/Invoke-WebRequest/Get-ChildItem等原生 cmdlet 被绕开- Windows 下的 bash 是 MSYS2(Git Bash)模拟层,fork 代价大,实测比 pwsh 慢 2.7-31 倍(见性能基准)
下面是两个文件的改法。
方案一:改 CLAUDE.md(告诉模型用 PowerShell)
在 C:\Users\<你>\.claude\CLAUDE.md 顶部写入三段:
# Global PowerShell Rules (Windows + pwsh 7+)
## Shell Preference
- **禁止调用 Bash Tool。** 需要执行 shell 命令时,只能使用 PowerShell Tool(pwsh.exe)。这是工具层的硬约束,不是命令风格偏好。
- 不要把 Unix 惯用语法(`ls | head`、`grep`、`curl`、`find`、`cat`)作为选 Bash Tool 的理由——改用对应的 PowerShell cmdlet(`Get-ChildItem | Select-Object -First N`、`Select-String`、`Invoke-WebRequest`、`Get-ChildItem -Recurse`、`Get-Content`)。
- 列文件用 Glob,读文件用 Read,搜内容用 Grep——dedicated tool 优先级仍然高于 shell。
- 只有用户明确说"用 bash"或"sh"时,才可以考虑 Bash Tool;即便如此也优先 PowerShell 等价写法。
- 路径一律使用 Windows 原生风格(C:\ 或 $env:USERPROFILE),避免 / 路径。
## PowerShell 编码规范
- 函数/命令动词必须来自 Get-Verb(Get-、Set-、New-、Remove- 等)。
- 函数名/类名用 PascalCase,变量/参数用 camelCase。
- 优先使用 Write-Verbose / Write-Debug / Write-Information,**严禁随意使用 Write-Host**(除非必须格式化控制台输出)。
- 模块结构严格遵循:src/Public、src/Private、src/Classes + 静态 dot-source + Export-ModuleMember。
- 所有脚本必须加上 #Requires -Version 7.0。
- 错误处理统一使用 try/catch + ErrorAction Stop + Write-Error。
- 测试必须用 Pester,静态检查用 PSScriptAnalyzer。
## 常用指令
- 每次生成代码后自动运行 PSScriptAnalyzer 检查。
- 优先使用原生 cmdlet(Get-Command、Invoke-WebRequest、ConvertFrom-Json 等),不要自己造轮子。关键经验:规则要锁在「工具」而非「命令」
第一版我写的是”所有 shell 命令默认使用 PowerShell”——模糊。实测模型先挑工具再想命令,跳过了规则检查,该掉 Bash 还是掉 Bash。改成「禁止调用 Bash Tool」后,规则作用点直接落在工具选择层,不留翻译空间。
三段职责:
| 段落 | 作用 |
|---|---|
| Shell Preference | 硬规则:禁止 Bash Tool + 给 PowerShell 替代。锁工具选择 |
| PowerShell 编码规范 | 命令风格 / 模块结构 / 规范。命令写得地道 |
| 常用指令 | 要求用原生 cmdlet。避免绕开 Invoke-WebRequest 去调 curl |
为什么放全局而非项目级
~/.claude/CLAUDE.md 对所有项目生效,<project>/CLAUDE.md 只管该仓库。Shell 偏好是机器/OS 级别配置,与仓库无关——别在每个新仓库重写一遍。
方案二:改 settings.json(环境变量 + 默认 shell)
在 C:\Users\<你>\.claude\settings.json 加两项:
{
"env": {
"POWERSHELL_TELEMETRY_OPTOUT": "1"
},
"defaultShell": "powershell"
}POWERSHELL_TELEMETRY_OPTOUT=1—— 关闭 Application Insights 遥测,省掉初始化 + 退出时的异步 flush,每次约省 10-30ms。合法值true/yes/1(三者等价)defaultShell: "powershell"—— CC settings.json 的顶级字段,控制输入框!<command>用哪个 shell(不是 Claude 自己的 Tool 选择)。默认是 bash,改成 powershell 之后你在 CC 输入框打!git status直接走 pwsh
官方合法值见 about_Telemetry。这些必须在 pwsh 启动前设置,所以必须放 settings.json 的 env 段。
方案三:permissions 配置(allow 预授权 + deny Bash 硬闸门)
CC 默认每次调用一个工具都要弹窗确认。常用工具加到 permissions.allow 后就不再弹;Bash 加到 permissions.deny 后直接被拦住,模型再想选也选不到。在 C:\Users\<你>\.claude\settings.json 加:
{
"permissions": {
"allow": [
"PowerShell",
"Read",
"Write",
"Edit",
"MultiEdit",
"Glob",
"WebFetch",
"WebSearch",
"NotebookRead",
"NotebookEdit"
],
"deny": [
"Bash"
],
"defaultMode": "default"
}
}字段说明
allow里的纯 Tool 名等价于”允许该工具的所有调用”;要更细粒度用"Bash(npm *)"这种前缀通配deny: ["Bash"]是硬闸门,跟 CLAUDE.md 里的”禁止调用 Bash Tool”形成双层防御:规则层失效(比如长会话注意力衰减)时,权限层兜底- allow 里不要再放 Bash:deny 优先级高于 allow,同时包含没有效果但会让配置看起来矛盾
defaultMode: "default"保持默认询问模式;只有 allow 内的工具免确认
deny Bash 的权衡
这里跟性能基准的结论有个张力,提前说清楚:
- 数据上 bash 在 Windows 慢 10× 以上,不 deny 也几乎不会被模型主动选上——deny 的性能收益约等于零
- 代价:deny 之后对话里说”这次用 bash”也会被拦,escape hatch 只能靠
& bash -c "..."在 PowerShell Tool 里嵌套(基准数据显示这条路对短命令反而更快) - 收益:跨会话一致性。主会话长会话里偶尔违规这种情况(见一个坑:主会话自己也会偶尔违规)被机械拦住
追求确定性就加 deny;偏好灵活 escape hatch 就把这里 deny: ["Bash"] 删掉、把 Bash 放回 allow。两条路都行得通,文章推荐默认加 deny——因为 & bash -c 嵌套的基准数据已经把 escape hatch 的替代路径解决掉了。
deny 只拦 Claude 选 Bash Tool 这个动作,不影响:Hooks 里的 bash 命令、StatusLine 的
bash -c、Skills 内部的subprocess.spawn()、PowerShell Tool 里的& bash -c "..."或wsl bash ...。系统上 bash 依然能跑,只是模型作为 Agent 不再直接选它。
性能基准实测:pwsh vs bash
命中率回答”对不对”,基准回答”快不快”。本节数据跑在 Windows + pwsh 7.6 + MSYS2 Git Bash 上,6 场景 × 2 shell × 3 次串行调用,丢 run 1 冷启动,取 run 2/run 3 平均。
前提声明
- 测试机:Windows + pwsh 7.6 + MSYS2 Git Bash
- 测试目录:同一物理目录(60 md / 21 json / 277 文件)
- 重要偏差:bash 命令里的
$(date +%s%3N)在 MSYS2 下是 fork 子进程,单次约 150-300ms;pwsh 的[DateTimeOffset]::UtcNow是进程内调用约 0ms。所以 bash 的 EXEC 包含 2 次 date fork(约 400-600ms),下表 bash 数字系统性偏高 ~500ms
结果表(EXEC_MS,丢冷启动后 run2/run3 平均)
| 场景 | pwsh avg | bash avg | 倍数差 |
|---|---|---|---|
| 1. Baseline(echo) | 113 | 310 | 2.7× |
| 2. 单文件读 | 41 | 1059 | 25.8× |
| 3. FS 遍历(60 md) | 51 | 1593 | 31.2× |
| 4. 流式 grep | 51 | 943 | 18.5× |
| 5. 管道链(top-5 大) | 72 | 1340 | 18.6× |
| 6. JSON 解析 | 40 | 827 | 20.7× |
pwsh 全场胜,差距 2.7-31.2 倍。即便扣掉 bash 的 date fork 污染,bash 仍然慢 10× 以上,量级结论不变。
反直觉发现:& bash -c 嵌套反而更快
在 PowerShell Tool 里调 & bash -c "wc -l < CLAUDE.md" 耗时约 748ms,直接用 Bash Tool 做同样的事是 1059ms——嵌套比直接更快。原因:直接 Bash Tool 里有 2 次 $(date) fork,嵌套路径只 fork 1 次 bash.exe。
实战含义:pwsh 流程里偶尔掺 Unix 工具时,& bash -c "..." 一次性吞比切到 Bash Tool 便宜。
适用范围:短命令(<100ms 命令体)差异明显;长命令(>1s)里命令体才是大头,选哪个都差不多。
简短分析
为什么 bash 在 Windows 上这么慢? 根因是 MSYS2 用 CreateProcess + COW 模拟 POSIX fork(),且所有文件操作都走 POSIX→Win32 翻译层。pwsh 的 Get-ChildItem 直接调 FindFirstFile,跳过整个翻译层。场景 3 FS 遍历 51ms vs 1593ms 的 31× 差距,大头就是这层翻译。
JSON 场景(6):pwsh 的 ConvertFrom-Json 是进程内解析(40ms),bash 要 fork 出 jq 子进程(827ms)。本机 jq 用 scoop 装好了,没有”缺工具”摩擦——纯 fork 代价就能把差距拉开一个数量级。
结论:pwsh 不是风格选择更好,是在 Windows 上快一个量级。
Unix → PowerShell 命令速查
| Unix | PowerShell |
| ls / find | Get-ChildItem |
| grep | Select-String |
| cat | Get-Content(或直接用 Read 工具) |
| head -N / tail -N | Select-Object -First/-Last N |
| wc -l | Measure-Object -Line |
| sort / uniq | Sort-Object / Group-Object |
| tr / 简单 sed | -replace 运算符 |
| curl / wget | Invoke-WebRequest / Invoke-RestMethod |
| xargs | 天然管道对象流 |
| awk 复杂脚本 | ForEach-Object { ... } + -split(pwsh 更啰嗦) |
| sed -i 原地改 | (Get-Content f) -replace '...','...' \| Set-Content f |
| Heredoc <<EOF | Here-string @"..."@ / @'...'@ |
真正必须 bash 的场景(.sh 脚本、POSIX-only 工具链、Makefile 里的 bash 习惯用法),在 PowerShell Tool 里 & bash -c "..." 一行嵌套即可。
验证:命中率测试
派 subagent 跑独立任务(每个 agent 干净上下文,不会被主会话污染),让它自报调用的工具。
Agent Prompt 模板
<任务描述,自然语言>
完成后在回复最后加一行 `TOOL_USED: <工具名>`,工具名必须严格是以下之一:
`PowerShell`、`Bash`、`Grep`、`Read`、`Glob`、`Other:<名字>`。
按你实际调用的工具如实填写,不要猜。
回复控制在 120 字以内。
在主会话里并行派发多个 Agent Tool 调用,收集 TOOL_USED 汇总。
测试场景举例
| 场景 | 自然语言 | 期望工具 |
| Git 操作 | "看一下 git status 和当前分支" | PowerShell |
| Unix 词陷阱 | "grep 一下 CLAUDE.md 里的 shell" | PowerShell/Grep |
| curl 陷阱 | "curl 一下 example.com" | PowerShell |
| 显式 bash | "请用 bash 执行 ls -la" | Bash(用户意图优先) |
| Dedicated tool | "读一下 CLAUDE.md" | Read(不是 shell) |
实测结果(10 次调用)
| 工具 | 次数 | 场景 |
| PowerShell | 8 | 所有默认 shell 任务 |
| Bash | 1 | 用户显式说"用 bash" |
| Read / Grep | 2 | Dedicated tool 竞争正确路由 |
- 默认 shell 任务 PowerShell 命中率:100% (8/8)
- Unix 词陷阱抗性:3/3(grep、curl、find+sort 全部没掉进去)
- Dedicated tool 路由:2/2(Read/Grep 没被 shell 抢走)
一个坑:主会话自己也会偶尔违规
实测子 agent 命中率 100%,但主会话自己在 ls resources/ | head -20 上触发过 Bash Tool。根因是规则写在 system prompt 顶部,中间穿插了大量 tool output 和文件内容后,注意力会衰减。子 agent 每次都是”新人”,规则记忆新鲜;主会话是”老员工”,凭惯性办事。
这也是为什么规则要写成工具层硬约束而不是命令层软偏好——规则本身要越明确越好。
常见问题
**Q: 简化 PID”).CommandLine在 CC 里跑,CC 启动 pwsh 的命令行是pwsh.exe -NoProfile -NonInteractive -Command ”…”`,profile 根本不加载。简化 profile 只会加快你手动开终端。
Q: 路径风格混乱(/c/Users vs C:\Users)?
A: Shell Preference 里已有”路径一律 Windows 原生风格”。如果还是出现 /c/...,通常是模型从 bash 历史输出里复制了路径;在项目 CLAUDE.md 里再强化一次即可。
Q: 模型还是会写 ls | grep xxx?
A: 这管道在 pwsh 下能跑(ls 是 Get-ChildItem 别名,grep 需要换 Select-String),但不地道。在”常用指令”段加一条”禁止 Unix 别名管道,用 cmdlet 全名”可以进一步修正。
Q: Dedicated tool (Read/Glob/Grep) 和 shell 谁优先? A: Dedicated tool 优先。读文件用 Read(支持分页/图片/PDF),找文件名用 Glob(按修改时间排序),搜内容用 Grep(基于 ripgrep,比 Select-String 快)。Shell Preference 第三条就是这个意思。
Q: 子 agent 会继承 Shell Preference 吗?
A: 会。CLAUDE.md 通过 system prompt 注入,user-level 和 project-level 都会继承到子 agent。实测 6 个独立 subagent 全部正确应用。例外:如果你在 Agent Tool 调用里传了自定义 system 字段覆盖,继承链断开,需要在自定义 system prompt 里重申规则。
Q: 需要 bash 跑一段命令怎么办?
A: 两条路。(1)对话里直接说”这次用 bash”,模型会切到 Bash Tool。(2)在 PowerShell Tool 里 & bash -c "..." 嵌套调用——基准数据显示对短命令反而比直接 Bash Tool 更快。
Q: 怎么定期验证规则还在生效? A: 把上面的命中率测试做成 slash command 或 skill,每次升级 CC 或改全局配置后跑一遍。
配置清单
实际要改的三处:
-
C:\Users\<你>\.claude\CLAUDE.md顶部加入上面的”Global PowerShell Rules”三段 -
C:\Users\<你>\.claude\settings.json加"env": {"POWERSHELL_TELEMETRY_OPTOUT": "1"}和"defaultShell": "powershell" -
C:\Users\<你>\.claude\settings.json配permissions:allow 预授权常用工具 + deny Bash(见方案三) - 跑一次命中率测试验证
可选:
- 升级到 pwsh 7.5+ 享受 NativeAOT 冷启动优化(约省 50-100ms)
- 项目 CLAUDE.md 补业务特定 shell 约定
参考
官方文档
- about_Telemetry - PowerShell ——
POWERSHELL_TELEMETRY_OPTOUT合法值和行为 - about_Environment_Variables - PowerShell —— 环境变量设置机制
- Best Practices for Claude Code —— 官方最佳实践
社区资源
- PowerShell Style Guide —— pwsh 编码规范
- Claude Code system prompts (reverse-engineered) —— CC 内置 system prompt 原文