非交互模式与脚本化
本教程共 32 篇 · 第 28 篇 · 更新于 2026-07-26 · 约 11 分钟阅读
28. 非交互模式与脚本化
本节目标:搞懂 codex exec 非交互模式—一句话丢进去、跑完吐结果就退出,怎么接进脚本和 CI/CD,怎么拿干净结果,怎么安全地无人值守运行。
前面讲的交互式 TUI 模式有个死穴:它假设有个活人坐在屏幕前盯着。可一旦你想「每天早上自动审一遍昨天的提交」「CI 跑挂了自动提修复 PR」「把报错日志管道喂给它要根因」—这些场景里压根没活人。codex exec 就是为「没活人」这件事生的。
exec 是什么
codex exec 是 Codex 的非交互模式。不开那个全屏 TUI 界面,一句提示词丢进去、跑完吐结果就退出,专为脚本和 CI 设计。
打个比方,交互模式像打电话—你来我往、随时插话。codex exec 像发邮件—你把要办的事在一封邮件里写清楚,发出去,对方关起门把事办完,回你一封信。你没法在它办事中途插嘴改需求,所以提示词得一次性写明白。
它专治三类场景:
- 重复到让人烦的活:每天给昨天的提交生成发布说明,写进脚本一键出
- 没有图形界面的环境:CI 流水线、云服务器、Docker 容器里起不了 TUI
- 要把输出喂给下一个程序:结果直接写进文件,或管道接给
gh pr comment贴 PR
最基础的用法
最简单的样子就一句话:
codex exec "总结这个仓库的结构,列出最该警惕的 5 个地方"
它会读当前工作目录、定计划、把过程往屏幕上滚,最后打出一句话总结,然后退出。不进 TUI、不停在界面等你按键。这就是「非交互」最直白的含义:跑完就走。
几个变体:
# 短别名 codex e,跟 codex exec 完全等价
codex e "解释这个项目是干什么的"
# 换个模型跑这一次
codex exec -m gpt-5.6 "审查当前改动,列出潜在 bug"
# 不把这次会话记录落盘
codex exec --ephemeral "快速过一遍这个仓库"
WarningCodex 硬性要求命令在一个 Git 仓库里跑。这是为了防止它在没有版本管理的地方造成不可逆破坏。非 Git 目录里跑会被拦下来,临时目录加
--skip-git-repo-check跳过检查—但只在确信环境安全时才这么干。
关键设计:进度走 stderr,结果走 stdout
这是 exec 能优雅接进管道的命门。
Codex 跑的过程会输出一大堆东西—它在想啥、跑了哪条命令、改了哪个文件。可你真正想要的往往只是最后那条总结。要是全混在一起,想「把结果写进文件」时,文件塞满过程噪音,没法用。
Codex 的解法:过程噪音走 stderr(标准错误),最终结果走 stdout(标准输出)。这俩是 Unix 的两条独立输出流,平时终端看它们混在一起,但管道 | 和重定向 > 默认只接 stdout。
# 结果既打到屏幕、又存进文件,过程噪音不会进文件
codex exec "给最近 10 个提交生成发布说明" | tee release-notes.md
常见操作对照表:
| 你想干的事 | 怎么写 | 原理 |
|---|---|---|
| 只把结果存文件 | codex exec "..." > out.md | > 只接 stdout |
| 结果既存又看 | codex exec "..." | tee out.md | tee 一份存盘、一份打屏 |
| 结果喂给下个程序 | codex exec "..." | pbcopy | 下游只收到干净结果 |
| 过程噪音也留存 | codex exec "..." > out.md 2> log.txt | 2> 单独收 stderr |
Warning别手贱用
2>&1把 stderr 并回 stdout。我第一次写脚本就这么干,文件里全是「正在思考」「正在读取文件」的过程行,最终总结被淹在里头。把2>&1去掉,文件立刻就干净了。
沙箱权限:默认是只读
这是最容易想当然写错的一点:codex exec 默认跑在只读沙箱里。它能读、能分析、能给建议,但默认不改你的文件、不执行有副作用的命令。要让它真动手,得显式放开。
为什么默认这么保守?因为它是为无人值守设计的,没人在旁边喊停。要是默认就放开写权限又没人盯,一个理解偏差就可能在你仓库里乱改一通。
三档对照:
| 沙箱档位 | 能干啥 | 什么时候用 |
|---|---|---|
read-only(exec 默认) | 只读、只分析,不改文件 | 代码审查、生成报告 |
workspace-write | 工作区目录内读写 | 要它真改代码、修 bug |
danger-full-access | 几乎不设限 | 只在隔离环境用 |
# 默认只读:审查改动,碰不到你的文件
codex exec "审查当前改动,列出潜在 bug"
# 放开工作区写权限:让它真去修
codex exec --sandbox workspace-write "修好失败的测试用例"
# 仅限隔离环境:几乎不设限
codex exec --sandbox danger-full-access "<隔离 runner 里的任务>"
Tip老脚本里的
--full-auto已弃用,新脚本直接用--sandbox workspace-write替代,意图更明确。danger-full-access只在隔离的 CI runner 或容器里用,在你日常开发机上对着重要项目甩这一档,等于交出万能钥匙。
还有两个给受控自动化用的开关:
--ignore-user-config:不加载$CODEX_HOME/config.toml,保证每台机器行为一致--ignore-rules:跳过用户级和项目级的 execpolicy.rules文件
Warning
--ignore-user-config会让写在 config.toml 里的认证配置也不生效。如果你的 API key 在那个文件里,加了它这次跑就报 401。它适合认证走环境变量或 CI Secret 的受控环境,本机练习别加。
机器可读输出:—json 和 -o
纯文本输出对脚本不友好。Codex 给了两个互补的工具。
—json:过程变事件流
加 --json,stdout 从一段自然语言变成 JSON Lines(JSONL)—每行一个独立 JSON 对象,Codex 每发生一个状态变化就吐一行。
codex exec --json "总结这个仓库的结构" | jq
事件类型包括 thread.started(线程开始)、turn.started / turn.completed / turn.failed(一轮的开始/完成/失败)、item.*(具体动作,如执行命令、改文件)、以及 error:
{"type":"thread.started","thread_id":"0199a213-81c0-7800-8aa1-bbab2a035a53"}
{"type":"turn.started"}
{"type":"item.completed","item":{"id":"item_3","type":"agent_message","text":"仓库包含 docs、sdk、examples 三个目录。"}}
{"type":"turn.completed","usage":{"input_tokens":24763,"output_tokens":122}}
有了这个,脚本里判断成功失败、提取它干了哪些事、统计 token 用量,全都有结构化字段可抓。
-o:捞最终总结到文件
很多时侯你只想要最后那段总结,落进一个文件给下一步用。加 -o 就行:
codex exec "提炼项目元信息" -o ./summary.md
Note
-o会把最终消息写进文件,同时还照常打到 stdout。它不抢 stdout,你想再管道接给别人也不耽误。
CI 里的黄金搭配是两个一起上:
# 既拿机器可读的进度,又拿一份最终的自然语言总结
codex exec --json -o summary.md "审查这个 PR 的改动"
| 你想要的 | 用哪个 |
|---|---|
| 程序逐步读它的动作、判断成败 | --json |
| 只要最终总结落一个文件 | -o <path> |
| CI 里两样都要 | --json + -o 一起上 |
还有个进阶的 --output-schema:给它一个 JSON Schema,让最终结果严格按你定义的字段结构产出,下游程序拿到的字段稳定可靠。
stdin 管道:把上游输出喂给它
太多场景是「先有一坨数据,再让 Codex 处理它」。构建挂了有一段报错日志、CI 跑完有一段输出,你不想手动复制粘贴进交互界面。
这里有两种姿势,分清楚就不会用错:
姿势一:提示词 + 管道
当你心里清楚要它干啥,只是想把某个命令的输出当材料喂进去时用这个。提示词是你写的指令,管道内容是上下文材料。
# 构建挂了,把报错管道喂给它要根因
npm test 2>&1 \
| codex exec "总结失败的测试,提出最小改动的修复方案" \
| tee test-summary.md
# 把长日志喂给它做根因分析
tail -n 200 app.log \
| codex exec "找出最可能的根因,给出接下来三步排查建议" \
> log-triage.md
姿势二:codex exec -
当整个提示词都是上一个命令动态拼出来的时用这个。省略提示词参数,Codex 就从 stdin 读;想强制这个行为用 - 当哨兵。
# 把文件里的内容当成整个提示词
cat prompt.txt | codex exec -
# 用脚本现拼一个完整提示词
printf "用 3 条要点总结这段错误日志:\n\n%s\n" "$(tail -n 200 app.log)" \
| codex exec -
Tip维护几个提示词模板文件(比如标准的 PR 审查提示词),需要时
cat 模板.txt | codex exec -直接跑。改提示词只改文件、不用动脚本,指令和脚本解耦,在团队里特别值。
resume:接着上次跑
非交互不代表只能一锤子买卖。一个流程可以分两段跑,靠 resume 把前后串起来。
# 第一阶段:先让它找问题
codex exec "审查这处改动有没有竞态条件"
# 第二阶段:接着上次,让它修掉刚找到的问题
codex exec resume --last "把你发现的竞态条件修掉"
--last 接当前工作目录下最近的那次会话。想精确接某次特定会话,把会话 ID 跟在后面:
codex exec resume <SESSION_ID> "继续上次的任务"
Note加了
--ephemeral不落盘的跑法没法 resume—都没存,接什么。要用两阶段流水线,就别加--ephemeral。跨目录找最近的会话加--all。
CI/CD 集成
GitHub Action
官方提供 openai/codex-action@v1,在 CI 任务里替你装好 Codex CLI、起好代理、跑一条 codex exec。你不用自己在 runner 上折腾安装和登录。
# .github/workflows/codex-review.yml
name: Codex pull request review
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
codex:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
with:
ref: refs/pull/${{ github.event.pull_request.number }}/merge
persist-credentials: false
- name: Run Codex
uses: openai/codex-action@v1
with:
openai-api-key: ${{ secrets.OPENAI_API_KEY }}
prompt-file: .github/codex/prompts/review.md
output-file: codex-output.md
post_feedback:
runs-on: ubuntu-latest
needs: codex
if: needs.codex.outputs.final_message != ''
permissions:
issues: write
pull-requests: write
steps:
- name: Post Codex feedback
uses: actions/github-script@v7
with:
github-token: ${{ github.token }}
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.pull_request.number,
body: process.env.CODEX_FINAL_MESSAGE,
});
env:
CODEX_FINAL_MESSAGE: ${{ needs.codex.outputs.final_message }}
action 的几个关键输入:
| 输入 | 干什么 | 备注 |
|---|---|---|
prompt / prompt-file | 任务描述 | 二选一,都给会报错 |
sandbox | 沙箱模式 | 默认只读,要改文件得显式写 workspace-write |
model / effort | 模型和推理强度 | 留空用默认,别写死型号 |
output-file | 最终消息存到磁盘 | 方便后续步骤上传 |
codex-args | 额外 CLI 参数 | JSON 数组如 ["--ephemeral"] |
safety-strategy | 安全策略 | 默认 drop-sudo,Windows 必须设 unsafe |
密钥安全三条红线
第一条:key 存进 GitHub Secrets,绝不硬编码。 公开仓库全世界都能看,你把真实 key 写进 YAML 提交,等于把家门钥匙照片发到朋友圈。扫密钥的爬虫一刻不停。
第二条:别把 key 设成 job 级环境变量。 就算你存了 Secrets,如果往 job 顶上 env: 一挂,这个 job 里跑的任何东西—你的测试、npm install 触发的脚本、甚至被投毒的第三方 action—都能顺手偷走你的 key。正确做法是只传给 codex-action 那一步。
第三条:管住触发者。 默认只有对仓库有写权限的人能触发。公开仓库尤其要管住,别让任意陌生人都能让 Codex 在你仓库上跑。
其他 CI 平台
# GitLab CI
codex_review:
image: node:20
before_script:
- npm install -g @openai/codex
script:
- codex exec "Review changes in this merge request" -o review.md
artifacts:
paths:
- review.md
// Jenkins Pipeline
pipeline {
agent any
stages {
stage('Code Review') {
steps {
sh 'codex exec "Review changed files" -o review.md'
}
}
}
post {
always {
archiveArtifacts 'review.md'
}
}
}
无人值守的安全要点
非交互模式跑在无人值守环境,几个安全要点得记住:
- 沙箱选能跑通的最窄那档:纯审查用
read-only,要改文件才给workspace-write - 清洗外部输入:把 PR 描述、commit 信息喂给 Codex 前,先审有没有 HTML 注释或隐藏文本(提示注入风险)
- Codex 放 job 最后一步跑:免得后面的步骤继承它可能造成的意外状态改动
- 设合理超时:避免卡死流水线
- 验证成功后再继续:检查返回码,失败要有回滚
Warning
read-only只是不让它改文件、不让它联网,但它仍以高权限运行。别只靠read-only来保护密钥,要护密钥靠的是drop-sudo(默认开,别在多租户 runner 关)或低权限用户那套。
小结
- 是什么:
codex exec是非交互模式,一句话丢进去、跑完吐结果就退出,专为「没活人盯着」的场景生 - 关键设计:进度走 stderr、结果走 stdout,
>/tee/ 管道默认只接到干净结果,别用2>&1把噪音并回去 - 沙箱默认只读:要改文件得显式
--sandbox workspace-write,danger-full-access只在隔离环境用,老的--full-auto已弃用 - 机器可读:
--json把 stdout 变 JSONL 事件流,-o把最终总结落文件且仍打 stdout,CI 里两个一起上 - stdin 管道:指令你现写用「提示词 + 管道」,整个提示词由上游生成用
codex exec - - resume:
codex exec resume --last接着上次跑,--ephemeral没法 resume - CI 集成:GitHub Action 用
openai/codex-action@v1,key 存 Secrets 不硬编码、别设 job 级 env、drop-sudo别关
下一章讲 Git 工作树与 GitHub 集成—Worktrees、PR Chat、GitHub Action、@codex 评论委派。