Codex VSCode插件-WSL环境下运行-即使在WSL里打开pwsh也总是找不到Win侧装的软件/开发工具等问题的解决方法

以下内容是我跟codex共同编辑的。 前情提要 佬们是否有遇到过在WSL里启动Codex,它调用命令查询你Win侧一些命令不能很容易的发现,然而你手动在powershell里执行却不会报错?这不只是没加.exe的问题,因为在WSL里启动cmd/powershell来查询Win侧命令,本就不应该出现问...
Codex VSCode插件-WSL环境下运行-即使在WSL里打开pwsh也总是找不到Win侧装的软件/开发工具等问题的解决方法
Codex VSCode插件-WSL环境下运行-即使在WSL里打开pwsh也总是找不到Win侧装的软件/开发工具等问题的解决方法

以下内容是我跟codex共同编辑的。

前情提要

佬们是否有遇到过在WSL里启动Codex,它调用命令查询你Win侧一些命令不能很容易的发现,然而你手动在powershell里执行却不会报错?这不只是没加.exe的问题,因为在WSL里启动cmd/powershell来查询Win侧命令,本就不应该出现问题。

经过codex的头脑风暴,顺着以下路子找到了解决方法。

先总结一下:Codex插件的问题 :sweat_smile: :sweat_smile: :sweat_smile:

一次离奇的 PATHEXT=.CPL 排查记录:WSL、PowerShell、Codex VS Code 扩展和 WSLENV

最近遇到一个很迷惑的问题:在 Windows + WSL 环境里,从 Codex/VS Code 扩展的 WSL 会话启动 Windows 侧 pwsh.exe 时,PowerShell 里的 PATHEXT 居然只剩:

.CPL

这会导致 PowerShell 里查找 Windows 命令异常。比如 nssm 明明装好了,cmd.exe where nssm 能找到,但在这个特殊环境里:

Get-Command nssm

找不到。必须写成 nssm.exe 才行。

正常环境是什么样

先在普通 Windows PowerShell 里看:

$env:PATHEXT
[Environment]::GetEnvironmentVariable('PATHEXT', 'Process')
[Environment]::GetEnvironmentVariable('PATHEXT', 'Machine')
[Environment]::GetEnvironmentVariable('PATHEXT', 'User')

正常 Process 值是:

.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL

Machine 里少一个 .CPL

.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW

这本身没问题,PowerShell 进程里补上 .CPL 也正常。

普通 Ubuntu/WSL 里也没问题:

printf 'WSLENV=<%s>\nPATHEXT=<%s>\n' "$WSLENV" "$PATHEXT"
pwsh.exe -NoLogo -NoProfile -Command '$env:PATHEXT'

输出大概是:

WSLENV=<>
PATHEXT=<>
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL

也就是说,Linux 侧没有 PATHEXT 很正常;从普通 WSL 启动 Windows pwsh.exe 时,Windows 子进程会自然拿到 Windows 侧正确的 PATHEXT

异常只出现在 Codex/VS Code 扩展启动的 WSL 会话

在 Codex 会话里查:

printf 'WSLENV=<%s>\nPATHEXT=<%s>\n' "$WSLENV" "$PATHEXT"
pwsh.exe -NoLogo -NoProfile -NonInteractive -Command '$env:PATHEXT'

最初看到的是:

WSLENV=<PATHEXT/l:COMSPEC/p:SYSTEMROOT/p:...>
PATHEXT=<>
.CPL

关键线索是:

PATHEXT/l

/l 的坑 :sweat_smile: :sweat_smile: :sweat_smile:

WSLENV/l 是“路径列表”语义,不是“任意列表”。

测试:

cmd.exe /d /c "set FOO=.COM;.EXE;.BAT;.CMD&& set WSLENV=FOO&& wsl.exe -e printenv FOO"

能得到:

.COM;.EXE;.BAT;.CMD

但:

cmd.exe /d /c "set FOO=.COM;.EXE;.BAT;.CMD&& set WSLENV=FOO/l&& wsl.exe -e printenv FOO"

结果变量直接没了。

而真正的路径列表可以正常转换:

cmd.exe /d /c "set FOO=C:\Windows;C:\Users&& set WSLENV=FOO/l&& wsl.exe -e printenv FOO"

输出:

/mnt/c/Windows:/mnt/c/Users

所以问题很清楚:PATHEXT 虽然是一个“列表”,但它不是“路径列表”。它是 Windows 可执行扩展名列表,不能用 /l

进一步定位到 VS Code 扩展

继续查当前进程链:

ps -o pid,ppid,comm,args -p $$ -p $PPID
tr '\0' '\n' </proc/$PPID/environ | rg '^(WSLENV|PATHEXT)='

发现 Codex 会话的父进程是 VS Code 扩展里的 Codex app-server:

/mnt/c/Users/xxx/.vscode/extensions/openai.chatgpt-26.5429.30905-win32-x64/bin/linux-x86_64/codex app-server --analytics-default-enabled

然后在扩展文件里搜:

rg -n --hidden --no-ignore "PATHEXT/l|WSLENV|PATHEXT" \
  /mnt/c/Users/xxx/.vscode/extensions/openai.chatgpt-26.5429.30905-win32-x64

在打包后的 JS 里发现了关键逻辑:

$De=[{name:"PATHEXT",type:"list"},{name:"COMSPEC",type:"path"},{name:"SYSTEMROOT",type:"path"},...]

这个 type:"list" 会生成:

PATHEXT/l

于是根因就是:OpenAI/Codex VS Code 扩展把 PATHEXT 当成了 WSLENV path list 传递。

为什么不是改成 "PATHEXT"

一开始想把:

{name:"PATHEXT",type:"list"}

改成:

"PATHEXT"

但进一步验证后发现,更干净的做法是:完全不要通过 WSLENVPATHEXT

因为普通 WSL 里没有 PATHEXT,也没有 WSLENV 时,从 WSL 启动 Windows pwsh.exe 反而能自然得到完整值。

也就是说,不传 PATHEXT 不是禁用它,而是避免 WSLENV 覆盖/干扰 Windows 子进程自己的正常环境。

临时修复:patch 扩展文件

修改这个文件:

C:\Users\xxx\.vscode\extensions\openai.chatgpt-26.5429.30905-win32-x64\out\extension.js

WSL 路径:

/mnt/c/Users/xxx/.vscode/extensions/openai.chatgpt-26.5429.30905-win32-x64/out/extension.js

删除这一段:

{name:"PATHEXT",type:"list"},

删除后列表从:

$De=[{name:"PATHEXT",type:"list"},{name:"COMSPEC",type:"path"},...

变成:

$De=[{name:"COMSPEC",type:"path"},...

验证:

perl -0777 -ne '$c=()=/{name:"PATHEXT",type:"list"},/g; print "PATHEXT-list-occurrences=$c\n"' \
  /mnt/c/Users/xxx/.vscode/extensions/openai.chatgpt-26.5429.30905-win32-x64/out/extension.js

node --check /mnt/c/Users/xxx/.vscode/extensions/openai.chatgpt-26.5429.30905-win32-x64/out/extension.js

结果:

PATHEXT-list-occurrences=0
node --check 通过

重启 VS Code/Codex 后,再测:

printf 'WSLENV=<%s>\nPATHEXT=<%s>\n' "$WSLENV" "$PATHEXT"
pwsh.exe -NoLogo -NoProfile -NonInteractive -Command '$env:PATHEXT'
pwsh.exe -NoLogo -NoProfile -NonInteractive -Command 'Get-Command nssm -ErrorAction SilentlyContinue | Select-Object -ExpandProperty Source'

修复后:

WSLENV=<COMSPEC/p:SYSTEMROOT/p:...>
PATHEXT=<>
.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC;.PY;.PYW;.CPL
C:\Users\xxx\AppData\Local\Microsoft\WinGet\Packages\NSSM.NSSM_Microsoft.Winget.Source_8wekyb3d8bbwe\nssm-2.24-101-g897c7ad\win64\nssm.exe

成功。

长效保护:在 WSL 的 .profile 里清掉错误项

扩展更新后,extension.js 的本地修改可能会被覆盖。所以又加了一层长效保护,放在:

/home/xxx/.profile

加入:

# Work around the OpenAI/Codex VS Code extension passing PATHEXT via WSLENV.
# PATHEXT is a Windows executable-extension list, not a WSL path/list value;
# carrying it through WSLENV can break Windows command lookup from WSL.
if [ -n "${WSLENV:-}" ]; then
    _codex_wslenv_new=
    _codex_wslenv_old_ifs=$IFS
    IFS=:
    for _codex_wslenv_entry in $WSLENV; do
        case "$_codex_wslenv_entry" in
            PATHEXT|PATHEXT/*)
                continue
                ;;
        esac
        _codex_wslenv_new="${_codex_wslenv_new:+$_codex_wslenv_new:}$_codex_wslenv_entry"
    done
    IFS=$_codex_wslenv_old_ifs
    export WSLENV=$_codex_wslenv_new
    unset _codex_wslenv_new _codex_wslenv_old_ifs _codex_wslenv_entry
fi

.profile 是 WSL 用户的 login shell 启动脚本。Codex 扩展启动 WSL 里的 Codex 时用了类似:

/usr/bin/bash -lc ...

这里的 -l 会读取 .profile,所以这段保护会在 Codex 真正运行前生效。

验证:

bash -n ~/.profile

模拟扩展又带回错误值:

env -u PATHEXT WSLENV='PATHEXT/l:COMSPEC/p:SYSTEMROOT/p:SYSTEMDRIVE:USERNAME' \
  bash -lc 'printf "WSLENV=<%s>\nPATHEXT=<%s>\n" "$WSLENV" "$PATHEXT"; pwsh.exe -NoLogo -NoProfile -NonInteractive -Command "$env:PATHEXT"'

结果里 PATHEXT/l 被移除了,pwsh.exe 里的 PATHEXT 也正常。

可能副作用

这个 .profile 保护逻辑会移除 WSLENV 里的:

PATHEXT
PATHEXT/...

对日常使用基本是正向的,因为 PATHEXT 不应该通过 WSLENV 传。普通 WSL 不传 PATHEXT 时,Windows 子进程自己能拿到正确值。

理论副作用是:如果某个工具非常特殊,故意想通过 WSLENV=PATHEXT... 从 WSL 向 Windows 子进程传一个自定义 PATHEXT,这段逻辑会拦掉它。不过这个场景很少见。

总结

这次问题的关键不是 Windows 配置坏了,也不是 WSL 坏了,更不是 PowerShell 坏了。

真正的问题是:

Codex VS Code 扩展把 PATHEXT 当成 WSLENV 路径列表传递了

错误形式:

PATHEXT/l

正确处理:

不要通过 WSLENV 传 PATHEXT

临时修扩展,长效在 .profile 里做防护。修完以后,pwsh.exePATHEXT 正常,Get-Command nssm 也正常。

1 个帖子 - 1 位参与者

阅读完整话题

来源: linux.do查看原文