Claude Code + WSL 混合开发踩坑复盘
根因(一句话)
npm 是 Windows 的,npm install -g @anthropic-ai/claude-code 装了 Windows 版 Claude Code。
Windows 版 Claude Code 的 Bash 工具走 Git Bash (MSYS2) + 9p 协议访问 WSL 文件,导致文件读写缓存不一致。整个两天的排查链条从这里开始。
已验证的修复:source ~/.nvm/nvm.sh 切到 WSL 原生 Node 后,确认 which npm 是 WSL 路径,再重装 Claude Code。
🤝 备选方案:Windows 版 Claude Code + WSL 项目也能跑
发现根因前,我和 DeepSeek v4 Pro 已经把这套流程跑通了。 如果你不想重装 Claude Code,只要 100% 严格按规则来,也能正常开发。
核心妥协
承认 Bash 工具的 9p 缓存问题不可避免
所有涉及 WSL 文件的操作,强制走
wsl -e bash -c绕开 9p 缓存
完整规则(必须严格遵守)
| 操作 | 必须这么做 |
|------|-----------|
| 写代码 | Edit/Write 工具(写入是对的) |
| 验证文件内容 | wsl -e bash -c "grep/cat/ls ..." |
| 启停服务 | wsl -e bash -lc "nohup ... &" |
| 清 Python 缓存 | wsl -e bash -c "find ... -name __pycache__ ..." |
| ❌ 绝对禁止 | 直接用 Bash 的 ls/grep/cat 判断文件状态 |
为什么没继续用这套方案
反人性:Claude Code 默认就是用 Bash 工具,你必须时刻提醒它"用 wsl -e bash -c"
不可靠:AI 随时可能"忘记"规则,直接用 Bash 读文件,导致错误结论
不是默认模式:这是和 DeepSeek 硬怼出来的 workaround,不是 Claude Code 设计的使用方式
⚠️ 待验证方案(来自千问):禁止 WSL 继承 Windows 的 PATH
/etc/wsl.conf
[interop]
appendWindowsPath = false
理论上能从源头解决问题,但我们还没实际验证。核心还是 npm 的执行环境——只要
which npm指向 WSL 原生路径,就没问题。
🧠 心路历程(与 DeepSeek v4 Pro 复盘)
第一阶段:信心满满 → 怀疑人生
开始:用 VS Code Remote-WSL 开发是主流配置,Claude Code 这么先进的工具肯定没问题
踩坑:文件改了不生效,同样的命令一会对一会错,开始怀疑自己
崩溃点:连续两天卡在同样的问题,差点卸载 WSL 回到纯 Windows 开发
第二阶段:不甘心 → 追问"为什么"
内心挣扎:VS Code + WSL 这么多人用,肯定是常见问题,为什么这么先进的大模型都不知道?
关键提问:为什么别人没踩到这个坑?还是说大家都踩到了但没人说?
决定:周六花半天时间,和 DeepSeek v4 Pro 耐心复盘,不解决不罢休
第三阶段:复盘的两个关键突破
突破一:Bash 工具的真身
DeepSeek 引导我注意:
Bash 工具执行时的路径里有
D:\soft\Git\...——这不是 WSL 的 bash,这是 Windows 的 Git Bash!
为什么之前没发现?
我一直以为"在 WSL 目录下启动的 Claude Code"就是 WSL 版的
Bash 命令能执行 WSL 的文件,看起来没问题
直到对比:
wsl -e bash -c执行结果和 Bash 工具执行结果不一样
突破二:npm 把我骗了
DeepSeek 追问源头:
你在 WSL 下执行
npm install,但which npm返回的是什么?
$ which npm
/mnt/c/Program Files/nodejs/npm # 竟然是 Windows 的 npm!
最隐蔽的坑:
WSL 会把 Windows 的 PATH 继承过来
我在 WSL 里敲
npm,执行的是/mnt/c/下的 Windows npmnpm install -g装到了 Windows 的全局目录,而不是 WSL 的所以即使在 WSL 终端启动
claude,跑的还是 Windows 版 Claude Code
🎯 复盘关键点
为什么这个坑这么难踩中?
层层伪装:Windows 版 Claude Code + Git Bash + 9p 协议,三层叠加看起来"几乎正常"
概率性失败:缓存有时命中有时不命中,无法稳定复现
经验误导:"在 WSL 目录下运行 = WSL 原生"这个直觉是错的
大模型盲区:Claude/DeepSeek 都不知道自己的 Bash 工具在 Windows 下会走 9p 协议
DeepSeek 复盘方法论(值得保存)
先画架构图,把每个组件跑在哪个 OS 上标清楚
对每个"理所当然"的假设做校验:
which npm、which bash、echo $PATH同样的操作,换两种方式执行,对比输出差异
追根溯源:从"命令在哪装的"开始查,而不是从"命令为什么出错"开始
背景
项目:服装店管理系统(Vue 3 + FastAPI + SQLite)
我的环境:Windows 11,VS Code Remote-WSL 连接 Ubuntu
AI 工具:Claude Code(CLI),安装在 WSL 内但用的是 Windows 版 npm 装的 Windows 版
项目路径:
/home/suwr/project/clothes_shop/(WSL ext4 文件系统)
遇到的症状
| # | 症状 | 看起来像 |
|---|------|---------|
| 1 | 用 Edit 工具改了 models/__init__.py 加关系定义,Python 加载不到 | 代码没写进去 |
| 2 | Bash grep 说某段代码不存在,Read 工具却说存在 | 文件丢了 |
| 3 | pkill/fuser 杀不掉 uvicorn 进程 | 权限问题 |
| 4 | uv run uvicorn 启动后,admin/admin123 一会能登录一会不能 | bcrypt 坏了 |
| 5 | API 返回 403 但数据库权限正常 | 代码没生效 |
| 6 | 改了 auth.py 的错误消息,重启后还是旧消息 | 没重启成功 |
| 7 | Write 工具写文件,我的 Bash 看不到,用户的终端能看到 | 写到不同的地方了 |
排查过程(耗费约 4 小时)
第一轮:怀疑 Edit 工具写不进去
Read 能看到 → 改用了 Bash 的
sed/python -c改了 5 个文件,部分生效部分不生效
结论:不可靠,但不知道为什么
第二轮:怀疑 Windows vs WSL 路径
发现
uv run python报错路径是D:\soft\Git\...以为是 Windows Python 和 WSL Python 两套环境
重建了
.venv用 WSL 原生 Python问题依旧
第三轮:发现 __pycache__ 问题
清了就能工作,不清就可能加载旧代码
以为根因是 Python 缓存机制
但还是有时清了对有时不清也行
第四轮:关键突破
Write 工具写
wsl_file_operations.md,我的 Bashls说文件不存在用户在 WSL 终端里却能看见
意识到:不是"没写进去",是Bash 读有缓存
第五轮:找到根因和正确工具
wsl -e bash -c直接在 WSL 内核执行,不走 Windows 9p 协议验证:同样的
grep,Bash 看不到,wsl -e bash -c能看到确认:Edit/Write 写入正常,纯 Bash 读取有 9p 目录/元数据缓存
第六轮:解决服务启动问题
wsl -e bash -c "nohup uv run uvicorn ... &"→ 进程随 session 退出被杀死查日志:
nohup: failed to run command 'uv': No such file or directory原因:非交互 bash(
-c)不加载.bashrc,PATH 里没有~/.local/bin/uv修复:用
bash -lc(login shell)替代bash -c
根因总结
架构:
┌─────────────────────────────────────────┐
│ Windows 侧 │
│ ├── Claude Code 进程 │
│ ├── Bash 工具 → 走 9p 协议 → 读到旧缓存 │
│ └── Edit/Write → 走 9p 协议 → 写入正常 │
├─────────────────────────────────────────┤
│ 9p 互操作层(有独立元数据缓存) │
├─────────────────────────────────────────┤
│ WSL 侧 │
│ ├── ext4 文件系统(真实文件) │
│ ├── 用户终端 → 直接读写 ✅ │
│ └── wsl -e bash -c → 直接读写 ✅ │
└─────────────────────────────────────────┘
两个根因:
Bash 工具的 9p 缓存:Claude Code 的 Bash 工具是 Windows 进程,通过 9p 协议读取 WSL 文件时,目录列表和时间戳可能被缓存。导致看到过时的文件状态。
非交互 bash 的 PATH:
wsl -e bash -c不加载.bashrc,找不到~/.local/bin/uv。用bash -lc解决。
最终可靠工作流
| 操作 | 方法 |
|------|------|
| 读文件验证 | wsl -e bash -c "grep/cat/ls ..." |
| 写代码 | Edit/Write 工具 |
| 清缓存 | wsl -e bash -c "find ... -name __pycache__ ..." |
| 启服务 | wsl -e bash -lc "cd ... && nohup uv run uvicorn ... &" |
| 停服务 | wsl -e bash -c "kill \$(lsof -ti :8000)" |
| 测试 | curl |
关键教训
技术教训
AI 使用任何工具,特别是 Bash,一定要先搞清楚工具的运行环境是谁,是否和你开发环境一致——如果环境不一致,工具看到的东西可能不是真的
wsl -e bash -c是所有 WSL 内部操作的入口——读写都正确症状相同,根因可能不同——403 既可能是权限问题也可能是缓存问题
先对齐"谁看到了什么"再动手——一个文件,我的 Bash 和用户终端看到的不一样,这就是线索
bash -cvsbash -lc的 PATH 差异——如果登录 shell 能执行的命令在bash -c里找不到,就是这个原因
与大模型协作的教训
永远校验执行环境——大模型说"这是 WSL bash"不等于真的是,自己
echo $BASH验证大模型有盲区——Claude/DeepSeek 都不知道自己的 Bash 工具在 Windows + WSL 场景下的坑
不要让大模型主导排查方向——他们会在错误的假设上堆更多错误假设
复盘要找不同的模型——我卡了两天,换 DeepSeek v4 Pro 从头理,一小时就找到根因
最深刻的一条
-
如果"所有人都应该遇到但没人说",那一定是你哪里理解错了
VS Code + WSL 确实是主流
但主流的做法是:VS Code 在 Windows,** Claude Code 装在 WSL **
我是:VS Code 在 Windows,Claude Code 也在 Windows,然后跨 9p 访问 WSL——非主流配置
🚀 WSL 开发环境快速自检清单
下次开新项目前先跑一遍:
# 1. 确认 node/npm 是 WSL 原生的
which node # 应该是 /home/xxx/.nvm/... 不是 /mnt/c/...
which npm
# 2. 确认 claude 装在哪
which claude
npm ls -g @anthropic-ai/claude-code
# 3. 确认 Bash 工具的执行环境
# 在 Claude Code 里执行:echo $PATH | grep -i 'Git\|MSYS\|Program Files'
# 如果有输出 → 这是 Windows Git Bash,不是 WSL bash
# 4. 文件一致性校验(可选)
# Claude Code 写完文件后,用 wsl -e bash -c 再读一遍验证
💡 最后一句话
WSL 的继承 PATH 是万恶之源。 任何你在 WSL 敲的命令,先 which 一下确认是不是 Windows 的。这能帮你省掉两天的人生。