用 `codex exec` 把任务写成可重复执行的命令

2026-08-09

交互式用 Codex(OpenAI Codex)有个绕不开的问题:这次跑通了,下次还得从头把上下文、模式、目录再敲一遍。同事问你「你上回怎么让它做的」,你只能凭印象复述。真正能沉淀下来的不是对话,是一条能贴进 README、能塞进 npm script、能让别人原样复制的命令

Codex CLI 里干这件事的入口是 codex exec(别名 e),help 里的一句话说明是 “Run Codex non-interactively”。下面这些选项释义都取自 codex-cli 0.147.0(Windows 11)上 codex exec --helpcodex --help 的原文。

一、命令怎么写

先说一件写脚本时最容易踩的形式问题:选项分两类,位置不一样codex --help 列的是顶层选项-C / -s / -a / -c / --add-dir 这些),按用法行 codex [OPTIONS] <COMMAND> [ARGS] 的形态,它们写在子命令 exec 之前codex exec --help 列的才是 exec 独有的(--skip-git-repo-check / --color / --json / -o / --output-schema / --ephemeral / --ignore-user-config / --ignore-rules),写在 exec 之后。本机 0.147.0 上官方给的 -c 用法示例也是这个形态,例如 codex -c 'features=[unclosed' doctor --summary——-c 在子命令前面。

下面这条骨架就按这个形态写,Git Bash / macOS / Linux 通用:

codex \
  -C <你的仓库目> \
  -s read-only \
  -a never \
  exec \
  --skip-git-repo-check \
  --color never \
  -o ./codex-out/last-message.md \
  "把 src 目录下所有对外导出的函数整理成一张表,只输出表格,不要改任何文件"

Windows PowerShell 下换行符不同,续行用反引号:

codex `
  -C <你的仓库目录> `
  -s read-only `
  -a never `
  exec `
  --skip-git-repo-check `
  --color never `
  -o .\codex-out\last-message.md `
  "把 src 目录下所有对外导出的函数整理成一张表,只输出表格,不要改任何文件"

选项位置未逐项实测,以 codex --helpcodex exec --help 的当次输出为准;把命令抄进脚本前,建议先用这两条 help 核一遍你手上这个版本的分组。

逐个说为什么这几项在这里:

  • -C, --cd <DIR>(顶层):显式指定 agent 的工作根目录。写脚本时最怕的就是「在哪个目录下跑」这件事靠调用者的当前路径决定,把它钉死,别人从任何位置执行结果都一样。
  • -s, --sandbox <SANDBOX_MODE>(顶层):取值只有三个,read-only / workspace-write / danger-full-access。这条命令只是让它读代码出表格,不需要写文件,就给最小的 read-only——官方对这一档的原文是 “The agent can inspect files, but it can’t edit files or run commands without approval.”
  • -a, --ask-for-approval <APPROVAL_POLICY>(顶层):三档 untrusted / on-request / never。非交互场景没人在终端前面按回车,所以选 never(官方释义:从不询问,执行失败直接回传给模型)。这里有个高频误解要说清楚never 改变的只是「要不要问你」,它并不放开权限,沙箱边界仍然由 -s 决定。真正撤边界的是 danger-full-access 或那个 help 里被写成 “EXTREMELY DANGEROUS” 的 --dangerously-bypass-approvals-and-sandbox。把 -a never 当成「全放开」而不配 -s,是踩坑的起点。
  • --skip-git-repo-check(exec 独有):允许在非 Git 仓库里运行。如果你的目标目录本来就是仓库,这项可以不加;但脚本要发给别人用、不确定对方的目录形态时,加上能省一次莫名其妙的失败。
  • --color <COLOR>(exec 独有):always / never / auto,默认 auto。重定向进日志文件时选 never,否则日志里会混进控制字符,后面 grep 和 diff 都不好受。
  • -o, --output-last-message <FILE>(exec 独有):把 agent 的最后一条消息写到文件。这是「让下一步能接上」的关键,下一节细讲。
  • 位置参数 [PROMPT]:直接作为首轮指令带进去。提示词本身建议写得像验收标准,而不是像聊天——明确说「只输出什么」「不要动什么」。

让上游数据流进来

codex exec 的位置参数还有两种更适合脚本的用法,这两条行为是 help 里明确写了的:

不给参数、或者给一个 -,prompt 从 stdin 读:

cat ./prompts/review-checklist.md | codex exec -

而如果 stdin 是管道、同时又给了 prompt,stdin 的内容会作为一个 <stdin> 块追加到 prompt 后面:

git diff --staged | codex -s read-only -a never exec "对照下面这段 diff,逐条检查有没有漏处理的边界条件"

这个组合很实用:提示词是固定资产写死在脚本里,变动的部分(diff、日志、报错栈)由管道喂进去。你不需要每次去拼一个巨大的字符串。

需要写文件的时候

只有确实要改代码时才升到 workspace-write,并且用 --add-dir 精确追加主工作区之外的可写目录,而不是直接跳到 danger-full-access

codex \
  -C <你的仓库目> \
  -s workspace-write \
  --add-dir <额外需要写的目> \
  -a never \
  exec \
  -o ./codex-out/last-message.md \
  "只修改 tests 目录下的用例,其余文件不要动"

--add-dir-s 一样是顶层选项,所以同样落在 exec 前面。

另外还有一个 --approve-for-me:审批请求走自动复核,并使用 workspace-write 沙箱。它和手写 -s workspace-write 是两条不同的路径,选哪条取决于你要不要那层自动复核,别两个都堆上去然后猜谁生效。

把「可重复」做到底

一条命令要在别人机器上跑出同样的行为,最大的变量是对方的本地配置codex exec 有三个专门用来隔离环境的开关:

选项help 原文含义什么时候用
--ignore-user-config不加载 $CODEX_HOME/config.tomlauth 仍然使用 CODEX_HOME想让命令的行为完全由命令行决定时
--ignore-rules不加载用户或项目的 execpolicy .rules 文件排查「为什么他那儿被拦住了」时用来做对照
--ephemeral不把会话文件落盘批量跑、不想让 sessions 目录无限膨胀时

注意 --ignore-user-config 那句「auth 仍然使用 CODEX_HOME」——它隔离的是配置不是登录态,别指望靠它来切账号。

需要临时改某个配置项时,用顶层的 -c, --config <key=value>,点号路径表示嵌套,value 按 TOML 解析:

codex \
  -C <你的仓库目> \
  -s read-only -a never \
  -c model_reasoning_effort=low \
  -c hide_agent_reasoning=true \
  exec \
  -o ./codex-out/last-message.md \
  "..."

model_reasoning_effort 的取值官方给的是 minimal / low / medium / high / xhighhide_agent_reasoning 的官方说明是「在 TUI 与 codex exec 输出里抑制推理内容」——脚本里日志越干净越好,这一项就是为这个场景准备的。以上为按官方文档键位组合的示例,未逐项实测,以官方文档为准。

二、产出物长什么样

这是非交互模式最值钱的部分,也是最容易被想当然的部分。就 help 明确写了的来说:

  • --json:事件以 JSONL 打到 stdout。也就是一行一个 JSON 对象的流,不是一整个 JSON 文档,所以要按行解析,别整个文件丢给 json.loads具体有哪些事件类型、字段叫什么,help 里没写,本文也不编——你第一次接的时候自己打开文件看一眼字段名再写解析器,这一步省不掉。
  • -o, --output-last-message <FILE>:把 agent 的最后一条消息写到你指定的文件。要强调「最后一条」:中间过程不在里面。按 help 的释义,这两项作用于不同通道——--json 打到 stdout,-o 写进你指定的文件;但两者同时给会不会各自照常工作,我们没有实测,要用请以 codex exec --help 与你自己那次的实际输出为准,别把它当成既定结论写进团队脚本。
  • --output-schema <FILE>:接一个 JSON Schema 文件的路径,用来描述模型最终回复的结构。如果下游是程序而不是人,这条比在提示词里写「请输出 JSON」靠谱得多。

所以更稳的做法是先想清楚这一趟要哪一种产出,再挑一个。要过程流就走 --json,把 stdout 重定向进文件:

mkdir -p ./codex-out
codex \
  -C <你的仓库目> \
  -s read-only -a never \
  exec --json --color never \
  "..." \
  > ./codex-out/events.jsonl

要结论就走前面那条带 -o 的骨架,拿到的是一份可以直接贴 PR 评论、拼进周报、或者再喂给下一条命令的文件。注意 -o 只负责写文件,指定的目录不存在时要自己先 mkdir

顺带说一句会话本身。codex execresume 子命令(按 id 续,或 --last 续最近一次),会话文件默认落在 ~/.codex/sessions/ 下按年份分子目录。这意味着批量跑是有磁盘代价的:在本机 codex-cli 0.147.0(Windows 11)上,codex doctor --summary 的 Notes 区就报了 rollouts 占 405 个活动文件、3.07 GB。你要是打算让脚本一天跑几十次,--ephemeral 要么现在加,要么以后清盘的时候加。

三、怎么验收

命令写完别急着交给同事,按这个顺序自己过一遍。

第一步,确认配置真的加载成功了。 这是本机实测得到的一条很实用的结论:在 codex-cli 0.147.0(Windows 11)上执行 codex -c 'features=[unclosed' doctor --summary,命令没有崩溃退出,doctor 照常跑完,但结果里出现了这一行:

✗ config       config could not be loaded - Fix the reported config error, then rerun codex doctor.

也就是说,配置写坏了不一定会让你的命令当场失败,它可能只是悄悄没生效。所以「改完配置行为没变」的第一个动作应该是:

codex doctor --summary

去看 Configuration 分组下的 configauthsandbox 三行。本机这台的 sandbox 行显示的是 restricted fs + restricted network · approval OnRequest,能直接印证当前生效的边界。状态符号有四种: ok、 idle、 notes/warn、 fail,结尾还有一行形如 17 ok · 1 idle · 1 notes · 0 warn · 0 fail 的统计。要把诊断结果贴给别人看,用 codex doctor --json——官方对它的说明是 “Emit a redacted machine-readable report”,是脱敏的。

第二步,别指望 --strict-config 兜住一切。 --strict-config 的作用是 config.toml 里出现本版本不认识的字段时直接报错退出,听起来是拼写错误的保险丝。但本机实测:在 codex-cli 0.147.0(Windows 11)上执行 codex -c model_reasoning_effortt=high --strict-config exec --help(注意那个多打的 t),它正常打印了 help,没有报未知字段错误。结论是校验发生在真正加载配置去跑会话的时候,--help 这类不进入会话的路径不触发。所以拿 --help 做冒烟测试是测不出配置拼写错的。

第三步,检查引号有没有被 shell 吃掉。 -c 的 value 按 TOML 解析,help 里明写了解析失败则按字面字符串处理。按这条规则往下推:如果引号被 shell 处理掉、传进去的东西不再是合法 TOML,那它就会被当成字面字符串收下,而不是当场报错告诉你写错了——这一步我们没有实测,但既然规则是「不报错,降级成字符串」,就不能指望终端替你把关。判断方法还是回到第一步的 doctor,看配置那一行到底是什么状态。

第四步,看产出物本身。 -o 指定的文件到底写出来没有(目录不存在时先 mkdir);--json 的输出逐行是不是合法 JSON;--color never 有没有真的把控制字符去掉。

第五步,把版本号记进脚本注释。 本机采集时遇到过一件事:同一台机器,先执行 codex --version 得到 codex-cli 0.131.0,十几分钟后再执行同一条命令得到 codex-cli 0.147.0,而 which -a codex 全程只有一个可执行文件。Codex 有自更新能力,配置里 check_for_update_on_startup 默认就是 true。所以排查任何「昨天还好好的」问题,都必须以当次 codex --version 的实时输出为准,不要用记忆里的版本号。脚本里顺手打一行版本,将来排查省一半事。

把命令交出去之前,这四处值得逐一回看(这是命令行通用的检查清单,不是本机 codex exec 的实测统计):引号有没有被 shell 改写;-C 与相对路径的 -o 一起用时输出文件落到了哪个目录;-s 给的档位够不够这个任务真正做完;--color never 有没有漏加。

四、什么情况不适用

  • 需要人在旁边一步步确认的活儿别用它。 -a never 的官方释义是执行失败直接回传给模型,没有人工介入点。改数据库、动生产配置、执行不可逆操作这类,老老实实交互式跑。
  • 别为了「跑得顺」直接上 danger-full-access--dangerously-bypass-approvals-and-sandbox 后者 help 原文写的是仅用于本身已经在外部沙箱里的环境。在自己的开发机上把它写进一条要给全组复制的命令里,风险是整组的。
  • 对输出稳定性有硬要求的场景要人工兜底。 我们没有实测过同一条 codex exec 多次运行的输出一致性,也就不做任何承诺。要接程序,用 --output-schema 把结构约束住,然后在下游做校验和失败重试——把「模型这次听话了」当成前提是不行的。
  • 跨平台照搬要重新验。 官方对 features.unified_exec 标注的默认值是 trueWindows 除外;本机 codex-cli 0.147.0(Windows 11,2026-08-09)上 codex features list 给出的生效值确实是 false。同理,沙箱实现在 macOS 是 Seatbelt、Linux/WSL2 靠 bubblewrap、Windows 是原生受限 token,是三套东西。同事在 mac 上跑通的命令搬到 Windows,至少要重跑一遍 doctor。
  • 一次性的探索别硬写成脚本。 还没想清楚要什么的时候,交互式试比反复改命令行快。等到同一件事你手动做了第三遍,再固化成 codex exec,那时候提示词也磨得差不多了。

相关阅读


本文依据 Codex 官方文档(learn.chatgpt.com/docs/ 的《Non-interactive mode》《Configuration Reference》《Sandbox》页面)整理,核对日 2026-08-09;文中标注「本机实测」的部分基于 codex-cli 0.147.0 / Windows 11 环境下的只读命令输出。产品功能、模型与价格以官方最新说明为准。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。