终端环境与 Agent 会话环境说明

0 阅读4分钟

终端环境与 Agent 会话环境说明

这份文档的目标,是把下面三件事彻底讲清楚:

  1. 你在终端里敲一条命令时,系统到底用了哪一层环境。
  2. 你在 Codex 这类桌面 Agent 里执行命令时,用到的环境和终端有什么相同、有什么不同。
  3. Cursor / Claude Code / Codex 这类“桌面端 Agent”在原理上到底是不是一回事。

本文基于两类信息:

  • 本机实测结果
  • 官方公开资料

其中“本机实测”发生在 2026-04-09,工作目录是 ~/Documents/nook-ios


1. 一句话先讲明白

你平时“命令能不能跑通”,本质上取决于 5 层:

  1. 父进程传给你的环境变量
  2. Shell 启动时加载了哪些配置文件
  3. 当前工作目录是什么
  4. PATH、alias、function、shell builtin、可执行文件的查找顺序
  5. 当前宿主程序有没有额外的权限、沙箱、网络限制

终端、Codex、Cursor、Claude Code 的差别,大多都落在这 5 层上。


2. 一条命令到底怎么被执行

当你输入:

node -v

系统并不是“直接找到 node 就执行”。中间通常会经历这条链路:

  1. 一个宿主程序发起执行
    • 例如 Terminal、iTerm、Codex Desktop、Cursor、Claude Code CLI
  2. 它创建一个 shell 进程
    • 例如 /bin/zsh
  3. shell 先加载启动文件
    • 例如 ~/.zshenv~/.zprofile~/.zshrc
  4. shell 根据自己的查找规则解析 node
    • alias
    • shell function
    • builtin
    • PATH 里的可执行文件
  5. 命令在当前目录、当前权限、当前网络/文件系统约束下运行

所以“同一台机器里,同一个命令,在不同地方结果不一样”是正常的。最常见原因不是命令本身,而是:

  • PATH 不一样
  • ~/.zshrc 没加载
  • ~/.zprofile 没加载
  • 父进程环境不同
  • 工作目录不同
  • 工具运行在远程容器 / VM 而不是本机

3. zsh 的启动文件顺序

zsh 常见的加载顺序可以这样理解:

3.1 所有 zsh 都会读

  1. /etc/zshenv
  2. ~/.zshenv

这层最“底”。不管是不是 login shell,不管是不是 interactive shell,通常都会经过这里。

3.2 login shell 会额外读

  1. /etc/zprofile
  2. ~/.zprofile

这层适合放“登录时要准备好的环境变量”。

3.3 interactive shell 会额外读

  1. /etc/zshrc
  2. ~/.zshrc

这层适合放:

  • alias
  • prompt
  • 补全
  • 快捷函数
  • 交互体验增强

3.4 login shell 还可能再读

  1. /etc/zlogin
  2. ~/.zlogin

这层很少有人用。

3.5 login shell 退出时可能读

  1. /etc/zlogout
  2. ~/.zlogout

你这台机器上没有 ~/.zlogin~/.zlogout/etc/zlogin/etc/zlogout


4. 你这台机器上的 zsh 配置是怎么工作的

下面这些是本机已经验证到的事实。

4.1 ~/.zshenv 的作用

你的 ~/.zshenv 做了一件非常关键的事:

if [ -z "${ZPROFILE_SOURCED:-}" ]; then
  export ZPROFILE_SOURCED=1
  if [ -f "$HOME/.zprofile" ]; then
    source "$HOME/.zprofile"
  fi
fi

它的含义是:

  • 任何 zsh 一启动,先跑 ~/.zshenv
  • 如果还没有打过 ZPROFILE_SOURCED=1 这个标记
  • 就主动 source ~/.zprofile

这个设计的直接效果是:

  • 即使是非交互 shell,也能吃到你原本写在 ~/.zprofile 里的环境变量
  • 所以像 Codex / Claude Code / 各种 agent harness 这种“不会真的给你开一个交互终端”的工具,也更容易继承到 nvmbrewpyenv、Flutter、Android SDK 这些环境

这是你现在环境能工作得比较顺的核心原因之一。

4.2 ~/.zprofile 里放了什么

你的 ~/.zprofile 主要负责准备“开发环境变量”,包括:

  • Homebrew
  • rbenv
  • nvm
  • pyenv
  • Flutter / FVM
  • Android SDK
  • JAVA_HOME
  • DEVELOPER_DIR
  • 一些项目路径
  • ~/.local/bin

也就是说,你机器上的语言工具链环境,主要是靠 ~/.zprofile 建起来的。

4.3 ~/.zshrc 里放了什么

你的 ~/.zshrc 很轻,只做了交互层的事情:

alias setup-flutter='bash ~/.cursor/flutter_templates/setup_flutter_project.sh'

这意味着:

  • setup-flutter 只会在 interactive shell 里可用
  • 在 Codex 这种 zsh -lc 的非交互 shell 里,这个 alias 默认不会出现

这和你实际观测到的现象是一致的。

4.4 /etc/zprofile 在你机器上的作用

系统级的 /etc/zprofile 调用了:

/usr/libexec/path_helper -s

这一步会把系统默认路径拼进来。它是 macOS 上非常常见的一层 PATH 初始化。


5. 你平时在终端里敲命令,用的是哪套环境

这里先说“普通规律”,再说“你机器上的推断”。

5.1 普通规律

你在 Terminal / iTerm 新开一个窗口或 tab 时,通常拿到的是 interactive shell。

至于它是不是 login shell,要看终端配置:

  • macOS Terminal 通常默认偏向 login shell
  • iTerm2 可以配置成 login 或 non-login

所以你在真实终端里,很常见的情况是:

  • ~/.zshenv 会加载
  • ~/.zprofile 也可能会加载
  • ~/.zshrc 会加载

于是你既能拿到环境变量,也能拿到 alias / prompt / 补全。

5.2 对你机器的意义

因为你的 ~/.zshenv 会主动 source ~/.zprofile,所以即使某个终端 profile 不是 login shell,你依然大概率能拿到:

  • brew
  • nvm
  • pyenv
  • Flutter
  • Android SDK
  • Java

而如果那个终端是 interactive shell,你还会额外得到:

  • setup-flutter alias
  • 任何以后写进 ~/.zshrc 的交互增强

所以从“最终体感”看,你的本地终端环境非常像一套“无论 login 与否都尽量完整”的 zsh 环境。


6. 你当前这个 Codex 会话里,用到的环境是什么

这一节只讲本机已经验证过的事实。

6.1 当前命令是怎么执行的

我在这个会话里执行命令时,实际走的是:

/bin/zsh -lc '<command>'

这表示:

  • shell 是 zsh
  • -l,所以是 login shell
  • -c 直接执行一段命令
  • 不是一个真正给你人手打字用的 interactive shell

6.2 当前宿主进程是谁

当前 shell 的父进程是:

/Applications/Codex.app/Contents/Resources/codex app-server --analytics-default-enabled

也就是说,命令不是直接由 Terminal 发起的,而是由 Codex 桌面应用里的 app-server 拉起的。

6.3 当前会话已经验证到的环境事实

本机会话里已经确认到:

  • SHELL=/bin/zsh
  • TERM=dumb
  • PWD=/Users/huchu/Documents/nook-ios
  • node 来自 ~/.nvm/versions/node/v20.20.2/bin/node
  • node -vv20.20.2
  • nvm 在当前 shell 中是可用的 shell function

6.4 Codex 父进程本身就带着不少开发环境

我还检查了 Codex 的父进程环境,发现它自己已经带着这些变量:

  • PATH
  • NVM_DIR
  • NVM_BIN
  • JAVA_HOME
  • FLUTTER_ROOT
  • ANDROID_HOME
  • DEVELOPER_DIR
  • LANG
  • LC_ALL
  • ZPROFILE_SOURCED=1

这说明至少在你现在这台机器上,Codex app-server 不是一个“完全空白”的 GUI 进程。它已经带着一套开发环境在运行。

6.5 为什么当前 Codex 会话能用到你的 node

原因有两层,同时成立:

  1. Codex 父进程自身已经带着一部分开发环境
  2. 它再启动 /bin/zsh -lc 时,zsh 还会走你的启动文件

所以当前 shell 既继承了父进程环境,又跑了 zsh 初始化逻辑。

6.6 为什么你的 PATH 现在有重复

这是你当前配置里最值得注意的点。

根因是:

  1. ~/.zshenv 会先主动 source ~/.zprofile
  2. 当前 shell 又是 zsh -lc
  3. login shell 阶段还会再次处理 ~/.zprofile

而你的 ~/.zprofile 里有很多 export PATH=...,这些操作本身不是幂等的,所以路径会被重复追加。

于是你现在看到的 PATH 里出现了很多重复段,比如:

  • ~/.local/bin
  • Flutter 相关路径
  • Android SDK 相关路径
  • nvm / pyenv / rbenv 相关路径

这不会立刻让命令失效,但会带来几个副作用:

  • PATH 越来越长
  • 排障时更难看清“到底是哪一个 node 先被命中”
  • 某些初始化脚本会被重复执行

7. 为什么 Codex 里的 shell 和你手开的终端“不完全一样”

虽然它们都在你本机上,但不是同一种 shell 场景。

7.1 终端更像“人类交互 shell”

特点通常是:

  • interactive
  • 有 prompt
  • 有 alias
  • 有补全
  • 有颜色
  • 有历史记录体验
  • TERM 更像 xterm-256colorscreen-256color 这类值

7.2 Codex 当前会话更像“工具驱动 shell”

特点通常是:

  • 由宿主应用拉起
  • 通过 -c 执行命令字符串
  • 重点是可编排、可自动化、可抓取输出
  • 未必加载 ~/.zshrc
  • 未必有完整交互终端语义
  • TERM 可能是 dumb

所以你以后遇到这种情况时,不要惊讶:

  • 终端里能用的 alias,Agent 里不能用
  • 终端里 prompt 很漂亮,Agent 里没有
  • 终端里某些交互命令正常,Agent 里行为不一样

这不是“坏了”,而是宿主场景不同。


8. 桌面 Agent 的通用原理

这里说的是“抽象层面”。不同产品细节不同,但核心骨架非常像。

8.1 通用架构

大多数桌面 Agent 都可以用这条链路理解:

  1. GUI / IDE 宿主程序
  2. Agent harness
  3. 工具调用层
    • shell / terminal
    • 文件读写
    • 搜索
    • 浏览器 / 网络
    • git
  4. 模型推理层
  5. 权限 / 沙箱 / 审批 / 远程执行层

也就是说,真正决定“它像不像你的终端”的不是模型本身,而是工具层和执行层。

8.2 一定要分清两种模式

所有这类产品,几乎都可以拆成两大类:

  1. 本地前台 Agent
  2. 远程 / 后台 Agent

这两类看起来都像“AI 在帮你干活”,但环境来源完全不同。

本地前台 Agent

特点:

  • 命令在你的机器上执行
  • 读写的是你当前磁盘上的文件
  • 能不能找到 nodepythonxcodebuild,取决于你本机环境
  • 很像“一个会自动敲命令的终端伙伴”
远程 / 后台 Agent

特点:

  • 命令在远程 VM / container / worker 上执行
  • repo 常常是重新 clone 过去的
  • 环境是远端镜像、远端脚本、远端 worker 决定的
  • 默认不一定等于你本机的 PATHnvm、Xcode、Android SDK

这也是为什么很多人会说:

  • “本地 agent 能跑,cloud agent 跑不了”
  • “桌面端能找到 node,后台 agent 找不到”

不是模型能力变了,而是执行环境换了。


9. Codex、Cursor、Claude Code 到底是不是一回事

结论先说:

它们在“产品抽象”上很像,但在“执行落点”上不一定一样。

9.1 Codex Desktop

结合本机会话和 OpenAI 官方资料,可以这样理解:

  • 本地线程里,Codex 可以直接在本机工作区里读写、执行命令
  • 桌面应用负责线程、任务、diff、并行 agent、worktree 等编排
  • OpenAI 官方也明确把 Codex app 定义成多 agent 的 command center,并支持 worktrees / cloud environments

对你来说,最重要的是:

  • 本地线程不等于“裸终端”
  • 但它确实可以非常贴近你本机开发环境

9.2 Cursor

从 Cursor 官方公开资料能确认两件事:

  1. Cursor 有 cloud agents
  2. Cloud agents 运行在隔离虚拟机里;如果是 self-hosted 模式,则由 worker 接收来自 Cursor 云端的工具调用并在你的基础设施上执行

这意味着:

  • Cursor 不是只有一种执行模式
  • “编辑器里这个 agent”和“云上 background agent”不能混为一谈

基于官方资料和同类产品的共同结构,可以做一个合理推断:

  • Cursor 的本地前台 agent 更像“挂在编辑器里的本地工具执行器”
  • Cursor 的 cloud/background agent 则明显是“远程执行环境”

这里的“本地前台 agent”表述是基于产品结构的推断,不是本文直接实测出的结论。

9.3 Claude Code

Claude Code 官方公开形态更偏 CLI / agent harness,而不是一个像 Codex app 那样完整强调“桌面 command center”的产品外壳。

从 Anthropic 官方文档可以确认:

  • Claude Code 有层级化设置
    • ~/.claude/settings.json
    • .claude/settings.json
    • .claude/settings.local.json
  • Bash 工具会“在你的环境里执行 shell 命令”
  • 会话和工具状态可以本地保存并恢复

所以如果你是在本机终端或某个桌面壳里跑 Claude Code,本质上它仍然高度依赖宿主机器的 shell 环境。

9.4 最实用的理解方式

不要把它们简单理解成“所有桌面端都一样”。

更准确的理解是:

  • 它们共享同一类设计思路
    • 模型 + 工具 + 权限 + 工作区 + 自动化
  • 但具体执行在哪
    • 你本机
    • IDE 内嵌终端
    • 桌面 app 子进程
    • 远程 worker
    • 云端隔离 VM 才是真正决定环境差异的关键

10. 你这台机器上,最值得记住的几个结论

10.1 你的 node 当前来自 nvm

当前实测:

node -> /Users/huchu/.nvm/versions/node/v20.20.2/bin/node
node -v -> v20.20.2

10.2 你把“登录环境”推广到了所有 zsh

因为 ~/.zshenv 主动 source ~/.zprofile,所以你的非交互 zsh 也能拿到完整开发环境。

这对 Agent 非常友好。

10.3 你的 alias 仍然只属于交互 shell

setup-flutter~/.zshrc 里,所以:

  • 终端交互场景能用
  • Codex 当前这种 zsh -lc 场景默认不能用

10.4 你现在有 PATH 重复问题

问题不在于“能不能用”,而在于“长期可维护性”。

建议以后考虑做两件事中的一个:

  1. 把真正的环境变量初始化抽到单独文件,比如 ~/.shell_env
    • ~/.zshenv~/.zprofile 都去 source 它
    • 但只在一个地方真正做 PATH 去重
  2. 使用 zsh 的去重能力
    • 例如 typeset -U path PATH

如果你愿意,我可以下一步直接帮你把这套配置整理成“不重复 PATH、又兼容 Agent”的版本。


11. 以后自己排查环境问题,用这些命令最快

11.1 看当前 shell 是谁拉起来的

echo "$SHELL"
ps -p $$ -o ppid=
ps -p $$ -o command=

11.2 看父进程是谁

ppid=$(ps -o ppid= -p $$ | tr -d ' ')
ps -p "$ppid" -o command=

11.3 看当前环境变量

env | sort

11.4 看某个命令到底命中了谁

type node
which node
whence -va node

11.5 看 nvm / pyenv / brew 是否真的在当前 shell 生效

type nvm
type pyenv
type brew
echo "$PATH"

11.6 对比不同 shell 模式

zsh -lc 'type node; node -v'
zsh -ic 'type node; node -v'
zsh -lc 'alias'
zsh -ic 'alias'

这组对比非常适合回答一句话:

“这个问题,是 login / interactive 差异造成的吗?”


12. 官方资料与参考链接

12.1 OpenAI / Codex

OpenAI 在这些页面里明确提到:

  • Codex app 是多 agent 的 command center
  • 支持 worktrees
  • 也支持 cloud environments

12.2 Cursor

Cursor 官方在这篇文章里明确写到:

  • cloud agents 运行在隔离虚拟机里
  • self-hosted 模式下,worker 接收来自 Cursor 云端的工具调用并执行

12.3 Anthropic / Claude Code

Anthropic 官方在这些文档里明确写到:

  • Claude Code 有用户级、项目级、本地项目级配置
  • Bash 工具是在你的环境里执行命令
  • 会话历史和工具状态可以在本地恢复

13. 最后的心智模型

以后你可以用下面这句话快速判断一个 Agent 到底会不会“像你的终端”:

先问它的命令是在哪台机器、由哪个父进程、用哪种 shell 模式、带着哪份 PATH、在什么权限下执行的。

这 5 个问题一旦想清楚,大多数“为什么这里能跑、那里不能跑”的问题都会变得非常直接。