<p>Oh-My-Zsh 插件会二次覆盖环境变量,因其在 VSCode 终端启动后、Shell 初始化完成后执行 export 语句重设 PATH 等变量,覆盖了 settings.json 中通过 terminal.integrated.env.* 注入的初始值。</p>

为什么 Oh-My-Zsh 插件会二次覆盖环境变量
Oh-My-Zsh 的插件在加载时,会主动执行 export 语句设置环境变量(比如 PATH、EDITOR、GIT_SSH_COMMAND 等),而这些操作发生在 VSCode 终端启动后、Shell 初始化脚本执行完毕的阶段。此时 VSCode 已经通过 terminal.integrated.env.* 注入过一次变量,但插件又无条件重写,造成「二次覆盖」——尤其常见于 git、pyenv、asdf、nodenv 这类依赖 PATH 注入的插件。
典型表现是:echo $PATH 显示的路径顺序与 settings.json 中配置的不一致;which python 返回的是插件硬编码的路径而非你期望的 pyenv 版本;甚至 printenv CUSTOM_VAR 输出为空,尽管你在 settings.json 里明确写了它。
如何让插件不覆盖 VSCode 设置的变量
核心思路:不让插件无脑 export,而是做存在性判断。多数主流插件(如 pyenv、nodenv)本身支持跳过初始化逻辑,只需提前设置对应开关变量:
-
pyenv插件:在~/.zshrc中 插件列表之前 加上export PYENV_ROOT="/opt/pyenv"和export PYENV_SKIP_INIT=1 -
nodenv插件:加export NODENV_SKIP_INIT=1 -
asdf插件:加export ASDF_SKIP_PLUGIN_DEFAULTS=1 - 通用兜底:在插件加载前统一禁止无条件 PATH 覆盖:
if [ -z "$VSCODE_TERMINAL" ]; then export PATH="/custom/bin:$PATH" fi
注意:这些开关变量必须在 plugins=(...) 之前定义,否则插件加载时已生效,再设就晚了。
VSCode 设置和 .zshrc 如何协同生效
VSCode 的 terminal.integrated.env.* 配置只影响终端进程启动瞬间的环境,之后 Shell 自身加载 .zshrc 时,所有 export 都会重新计算。所以关键不是“谁先设”,而是“谁别乱改”。推荐组合策略:
- 在
settings.json中用terminal.integrated.env.linux(或.macos)设置基础变量,例如CUSTOM_VAR、EDITOR、LANG - 在
~/.zshrc开头显式声明VSCODE_TERMINAL检测逻辑:if [ -n "$VSCODE_TERMINAL" ]; then # 不重复初始化版本管理器、不重写 PATH 主干 export PYENV_SKIP_INIT=1 export NODENV_SKIP_INIT=1 fi
- 把真正需要动态拼接的 PATH 片段(如项目本地
./bin)放到.zshrc末尾,并加上if [ -d "./bin" ] && [[ ":$PATH:" != *":./bin:"* ]]; then export PATH="./bin:$PATH"; fi这类防护
远程 SSH 场景下最容易被忽略的点
当你用 Remote - SSH 连到 Linux 服务器,VSCode 本地的 settings.json 对远程终端 完全不生效,所有环境变量控制权都在远程的 ~/.zshrc 手中。此时如果远程也装了 Oh-My-Zsh,且没做 VSCODE_REMOTE 判断,就会发生双重覆盖。
务必在远程 ~/.zshrc 里加入:
if [ -n "$VSCODE_REMOTE" ]; then export PYENV_SKIP_INIT=1 export NODENV_SKIP_INIT=1 # 不加载可能污染 PATH 的插件,例如: plugins=(git common-aliases) # 而不是 (pyenv nodenv git ... ) fi
这个判断比 VSCODE_TERMINAL 更底层,能准确识别是否为 Remote - SSH 启动的终端。漏掉这步,你在本地调好一万遍都没用。


















