Claude Code + WSL 混合开发踩坑复盘

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 缓存

完整规则(必须严格遵守)

详见:WSL workflow - 简书

| 操作 | 必须这么做 |

|------|-----------|

| 写代码 | 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 判断文件状态 |

为什么没继续用这套方案

  1. 反人性:Claude Code 默认就是用 Bash 工具,你必须时刻提醒它"用 wsl -e bash -c"

  2. 不可靠:AI 随时可能"忘记"规则,直接用 Bash 读文件,导致错误结论

  3. 不是默认模式:这是和 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 npm

  • npm install -g 装到了 Windows 的全局目录,而不是 WSL 的

  • 所以即使在 WSL 终端启动 claude,跑的还是 Windows 版 Claude Code


🎯 复盘关键点

为什么这个坑这么难踩中?

  1. 层层伪装:Windows 版 Claude Code + Git Bash + 9p 协议,三层叠加看起来"几乎正常"

  2. 概率性失败:缓存有时命中有时不命中,无法稳定复现

  3. 经验误导:"在 WSL 目录下运行 = WSL 原生"这个直觉是错的

  4. 大模型盲区:Claude/DeepSeek 都不知道自己的 Bash 工具在 Windows 下会走 9p 协议

DeepSeek 复盘方法论(值得保存)

  1. 先画架构图,把每个组件跑在哪个 OS 上标清楚

  2. 对每个"理所当然"的假设做校验:which npmwhich bashecho $PATH

  3. 同样的操作,换两种方式执行,对比输出差异

  4. 追根溯源:从"命令在哪装的"开始查,而不是从"命令为什么出错"开始

背景

  • 项目:服装店管理系统(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,我的 Bash ls 说文件不存在

  • 用户在 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 → 直接读写 ✅        │

└─────────────────────────────────────────┘

两个根因:

  1. Bash 工具的 9p 缓存:Claude Code 的 Bash 工具是 Windows 进程,通过 9p 协议读取 WSL 文件时,目录列表和时间戳可能被缓存。导致看到过时的文件状态。

  2. 非交互 bash 的 PATHwsl -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 |

关键教训

技术教训

  1. AI 使用任何工具,特别是 Bash,一定要先搞清楚工具的运行环境是谁,是否和你开发环境一致——如果环境不一致,工具看到的东西可能不是真的

  2. wsl -e bash -c 是所有 WSL 内部操作的入口——读写都正确

  3. 症状相同,根因可能不同——403 既可能是权限问题也可能是缓存问题

  4. 先对齐"谁看到了什么"再动手——一个文件,我的 Bash 和用户终端看到的不一样,这就是线索

  5. bash -c vs bash -lc 的 PATH 差异——如果登录 shell 能执行的命令在 bash -c 里找不到,就是这个原因

与大模型协作的教训

  1. 永远校验执行环境——大模型说"这是 WSL bash"不等于真的是,自己 echo $BASH 验证

  2. 大模型有盲区——Claude/DeepSeek 都不知道自己的 Bash 工具在 Windows + WSL 场景下的坑

  3. 不要让大模型主导排查方向——他们会在错误的假设上堆更多错误假设

  4. 复盘要找不同的模型——我卡了两天,换 DeepSeek v4 Pro 从头理,一小时就找到根因

最深刻的一条

  1. 如果"所有人都应该遇到但没人说",那一定是你哪里理解错了

    • 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 的。这能帮你省掉两天的人生。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容