Hub Codex CLI 完整教學

第 4 篇 高手 · 第 10 章

非互動自動化:codex exec

到目前為止,你跟 Codex 都是「面對面」聊天:你打字、它回話、跑到一半要不要動手還會停下來問你。這一章要教你另一種完全不同的用法——不開對話視窗、把任務寫成一張紙條塞給它、它自己跑完吐結果

這正是把 Codex 變成「自動化工人」的關鍵一招。

10.0 一句話 + 一個比喻

codex exec 是 Codex CLI 的「非互動模式」——你給它一句指令,它不開聊天視窗、不等你按鍵,自己跑完、把結果印到畫面上就退出。

打個比方:

  • 平常的 codex(互動模式)像是把同事叫到你旁邊,你一句他一句,過程中他還會回頭問你「這個檔可以改嗎?」。
  • codex exec 像是寫一張工作紙條,塞進一台會自己動的機器,按下開始,它就照紙條一路做到底,做完把成果丟出來給你,中間不打擾你。

為什麼需要這種模式?因為很多場景根本沒有「你」坐在電腦前面

  • 半夜兩點,伺服器自動跑你排程的整理任務。
  • GitHub 上有人開了 Pull Request(程式碼修改提案),系統自動叫 Codex 幫忙審查。
  • 你寫一個腳本,把網路抓來的資料丟給 Codex 整理成表格,再存檔。

這些都沒有人在旁邊按 [Y/n]這就是 codex exec 的舞台。

小技巧

「互動 vs 非互動」是這整章的核心分界。記一句口訣就好:有人看著螢幕 → codex;沒人看著、要塞進腳本或 CI → codex exec

本章只談終端裡的 codex exec。GitHub Actions、雲端委派那些「接到生產線」的玩法,屬於第 11 章,我們這章專心把「一台機器自己跑」這件事學透。

10.1 codex exec 概念與退出碼

它到底做了什麼

官方對 exec 的定義是這樣寫的(逐字):

"Automate workflows or wire Codex into your existing scripts with the exec subcommand. This runs Codex non-interactively, piping the final plan and results back to stdout."

翻成白話:exec 子命令把 Codex 接進你現有的腳本,它會非互動地跑,並把最終計畫與結果送回 stdout(標準輸出,也就是終端機畫面)。

把這句話拆成三個你必須記住的特性:

特性白話解釋
非互動不開 TUI 聊天介面、不等鍵盤輸入,跑完就退出
結果走 stdout最終結果印到畫面,所以可以被 > 存檔、用 |(管道)接給下一個指令
退出碼可判斷成敗跑完留下一個「退出碼」,腳本可以用它接 && / || 判斷成功還失敗

第一次跑跑看

最簡單的形式就是 codex exec 後面接一句你的需求:

🍎 Mac / 🐧 Linux

codex exec "summarize the repository structure and list top 5 risky areas"

🪟 Windows(PowerShell)

codex exec "summarize the repository structure and list top 5 risky areas"

它會分析你目前所在的這個專案,把「結構摘要 + 最該注意的 5 個風險區」直接印出來,然後退出。全程不會問你任何問題。

小技巧

codex exec 有一個更短的別名 codex e,功能完全一樣,打字懶得多按幾個字時很方便。官方 CLI 參考頁逐字寫:「Use codex exec (or the short form codex e) for scripted or CI-style runs」。

退出碼:腳本怎麼知道它成功還失敗

每個終端指令跑完,都會留下一個叫退出碼(exit code)的數字。0 代表成功,非 0 代表出事了。這是所有 shell 腳本判斷上一步成敗的依據。

codex exec 在這方面有兩條官方明講的行為:

  • CLI 功能頁逐字:exec「the command exits non-zero if submission fails so you can wire it into scripts or CI.」——提交失敗就以非零退出,讓你可以接進腳本或 CI。
  • 非互動頁逐字:如果某個「必要的」MCP server(外掛工具,詳見第 9 章)啟動失敗,codex execexits with an error instead of continuing.」——直接以錯誤退出,不會硬著頭皮繼續跑

你可以這樣親眼看退出碼:

🍎 Mac / 🐧 Linux

codex exec "do something"
echo $?

🪟 Windows(PowerShell)

codex exec "do something"
echo $LASTEXITCODE

$?(Mac/Linux)或 $LASTEXITCODE(PowerShell)會印出剛才那個指令的退出碼。

重要提醒

官方目前沒有提供一張完整的「退出碼 0/1/2…各代表什麼」對照表。我們只能確定「提交失敗會非零」「必要 MCP 失敗會以錯誤退出」這兩條,其餘各碼的精確意義請以實機 codex exec ...; echo $? 實測為準,別寫死成斬釘截鐵的對照。

別只信退出碼:雙重判斷

正因為退出碼不是萬無一失的單一真相,CI 實務上的建議是「退出碼 + 產出檔案」雙重判斷:不只看它有沒有非零退出,還要看「它該產出的東西(例如一份報告、一個修補檔)到底有沒有真的生出來、是不是空的」。

這個「看 artifact(產出物)在不在」的思路,在第 11 章的自動修復範例會再用到——官方那個範例就是用「修補檔是不是非空」來判斷成功,而不是只看退出碼。

小技巧

這跟你平常用 Codex 的鐵則一樣:「跑完了」不等於「做對了」。多看一眼它真正吐出來的東西,比盲信一個數字可靠。

10.2 結構化輸出(--json / --output-schema / -o

預設情況下,codex exec 吐給你的是人看的自由文字。但如果你要用程式去接它的輸出(例如自動把結果存進資料庫、貼到 PR 留言、產生報表),自由文字就很難穩定解析。

這時候你需要「結構化輸出」。Codex 提供三個旗標,目的各不相同,先看一張對照表:

旗標簡寫它輸出什麼適合
--json整段過程的 JSONL 事件流(逐行 JSON,含每一步進度)想監看「它做了哪些事」、抓 token 用量
--output-schema最終訊息符合你給的 JSON Schema(固定欄位)要穩定欄位的報表 / metadata
--output-last-message-o最終訊息額外寫到一個檔案(同時也印到 stdout)存檔、給 CI 後續步驟讀

下面一個一個拆。

--json:看它每一步在做什麼

--json 會讓 stdout 變成逐行的 JSON 事件(這種「一行一個 JSON」的格式叫 JSONL,JSON Lines)。每完成一個動作,就吐一行事件出來。

官方列出的事件型別(逐字)有:

  • Thread / Turn 事件thread.startedturn.startedturn.completedturn.failed
  • Item 事件item.*(泛指,含 item.starteditem.completed
  • 錯誤事件error

item.* 涵蓋的內容包括:agent 訊息、推理、指令執行、檔案修改、MCP 工具呼叫、網路搜尋、計畫更新等。

官方給的一個事件範例長這樣(逐字):

{"type":"turn.completed","usage":{"input_tokens":24763,"cached_input_tokens":24448,"output_tokens":122,"reasoning_output_tokens":0}}

你可以看到它連 token 用量都報給你了。配上 jq(一個處理 JSON 的小工具)就能漂亮地解析:

🍎 Mac / 🐧 Linux

codex exec --json "summarize repo structure" | jq

🪟 Windows(PowerShell)

codex exec --json "summarize repo structure" | jq

重要提醒

官方目前只公開了「事件型別」和上面那個 turn.completed 範例,各種 item 型別的完整欄位並未逐一公開。所以別把某個欄位名寫死進你的腳本,先實跑 codex exec --json "..." | jq 採樣看看真實長相,以實機輸出為準

--output-schema:強迫最終結果照你的格式來

--output-schema <檔案路徑> 指向一個 JSON Schema 檔(描述「我要的結果該有哪些欄位、各是什麼型別」)。Codex 會讓最終訊息乖乖符合這個格式。

這在「我要穩定欄位的報表」場景超好用——例如每次都要產出 {專案名稱, 用到的語言} 這種固定結構,比解析一坨自由文字可靠太多。

官方的 schema 範例(逐字):

{
  "type": "object",
  "properties": {
    "project_name": { "type": "string" },
    "programming_languages": { "type": "array", "items": { "type": "string" } }
  },
  "required": ["project_name", "programming_languages"]
}

「嚴格 schema」是必答題,不是選答題

上面那個範例其實漏了一個容易被忽略的必要欄位。--output-schema 吃的是嚴格模式(strict mode)的 JSON Schema,有兩條硬性要求:必須加一行 "additionalProperties": false,而且"required" 陣列要把 properties 裡列出的每一個欄位都寫進去,一個都不能少。少了任何一條,Codex 不會幫你把結果硬湊成自由格式將就過去,而是直接回報 schema 驗證錯誤——這是你的 request 本身在送出那一刻就被拒絕,不是模型「答錯」的問題。很多從網路上複製貼上的「範例 schema」剛好就是漏了這兩條的寬鬆版,照抄下去大機率會炸。把上面的範例補齊成真正嚴格、能穩定跑的樣子:

{
  "type": "object",
  "properties": {
    "project_name": { "type": "string" },
    "programming_languages": { "type": "array", "items": { "type": "string" } }
  },
  "required": ["project_name", "programming_languages"],
  "additionalProperties": false
}

養成一個小習慣就能躲掉這個雷:每加一個欄位到 properties,就順手把它的名字加進 required,收尾前再檢查一次最外層有沒有 "additionalProperties": false。巢狀物件(properties 裡面又是一個 object)同一條規矩要逐層都套用,不是只顧最外面那一層。

-o:把結果順手存成一個檔

-o(全名 --output-last-message)把 assistant 的最終訊息寫到指定檔案——而且它同時還是會印到 stdout,不是二選一。

三招最常一起用:讓結果符合 schema、又存成檔給後續步驟讀。

🍎 Mac / 🐧 Linux

codex exec "Extract metadata" --output-schema ./schema.json -o ./output.json

🪟 Windows(PowerShell)

codex exec "Extract metadata" --output-schema .\schema.json -o .\output.json

小技巧

--json--output-schema 目的不同、可以並用--json 管「整段過程」的事件流(它做了哪些事),--output-schema 只管「最終訊息」的形狀(結果長怎樣)。一個看過程、一個看結果。

10.3 stdin、pipe、codex exec resume 串接

這一節教你把 codex exec 接進「資料流水線」,以及怎麼讓兩次執行接續同一段記憶

stdin 只讀一份完整 prompt:先把資料組好

codex exec 沒有「引號放指示、管道再自動附上資料」的雙輸入模式。先記住兩條互斥規則:

  1. 如果引號裡已有位置引數(prompt),stdin 內容會被忽略;這時不要把日誌或其他資料接在管道前面。
  2. 如果要讓外部文字成為 prompt 的一部分,就用單獨的 -,並先把「指示 + 已處理資料」組成同一份輸入。

含日誌範例:先遮罩,再把指示和資料一起送

日誌常藏著 token、密碼、cookie、Email、內網網址或使用者資料。先複製成 app.redacted.log,把這些值以固定文字取代後再重新打開檢查;不確定能不能提供時,改用測試日誌。不要把原始日誌、密鑰或個資直接送進 prompt,也不要把遮罩後的檔案提交到公開儲存庫。

🍎 Mac / 🐧 Linux

# app.redacted.log 必須已先遮罩;這次只做唯讀分析
{
  printf '%s\n\n' '以下是已遮罩的應用程式日誌。只分析可能原因與下一步;不要執行指令、修改檔案或連線。'
  tail -n 200 app.redacted.log
} | codex exec --sandbox read-only - > log-triage.md

🪟 Windows(PowerShell)

# app.redacted.log 必須已先遮罩;這次只做唯讀分析
$prompt = @'
以下是已遮罩的應用程式日誌。只分析可能原因與下一步;不要執行指令、修改檔案或連線。
'@
$prompt += "`n`n" + ((Get-Content .\app.redacted.log -Tail 200) -join "`n")
$prompt | codex exec --sandbox read-only - > log-triage.md

--sandbox read-only 限制的是 Codex 的可寫範圍;最後的 > log-triage.md 是終端機把回覆存成報告。請先確認該檔名不存在,避免覆寫你原本的分析結果。

這行看似合理,實際會忽略資料

只要引號裡已有 prompt,stdin 就不會被讀。所以下面這行的 context.txt 內容根本不會出現在這次對話裡

# context.txt 會被忽略:不要用這種寫法傳資料
cat context.txt | codex exec "do X"

需要資料時,請像前面的日誌範例一樣用 -,並且不要再額外給位置引數;兩者只能二選一。

從檔案讀取一份完整 prompt

當 prompt 很長、想存在一個檔案裡(例如 prompt.txt),用 - 告訴 Codex:「整句指令請從 stdin 讀」。官方逐字:「Use codex exec - when you want to force that behavior explicitly.」檔案一樣只能放可安全提供的內容。

🍎 Mac / 🐧 Linux

codex exec - < prompt.txt
cat prompt.txt | codex exec -
generate_prompt.sh | codex exec --json - > result.jsonl

🪟 Windows(PowerShell)

Get-Content prompt.txt | codex exec -

小技巧

把常用的長 prompt 存成 .txt,用 codex exec - < prompt.txt 一鍵餵進去,團隊就能共用同一份「標準工單」,不必每次手打。檔案只放可安全分享的指示;若含日誌、客戶資料或密鑰名稱以外的憑證,請先遮罩並確認不會被 Git 提交。

stdin 沒關好,CI 裡會卡到天荒地老

社群回報過一個在排程/CI 環境特別容易中招的情況(issue #20919):codex exec "你的 prompt" 明明已經用位置引數給了 prompt、根本用不到 stdin,但只要它是被某個「不會主動關閉 stdin」的父行程呼叫(例如某些 process supervisor、沒設定 stdin 的背景工作),它還是會傻傻等 stdin 送出結束訊號(EOF),從此卡住不動、不吐任何錯誤。這種卡死在 CI 裡特別難查,因為畫面上什麼異常都看不到,就是「跑到一半沒反應了」。

保險做法:只要這次沒有要用管道餵東西,就明講把 stdin 接到空裝置,讓它一開始就沒有懸念。以下是 Mac/Linux 寫法:

codex exec "do the task" < /dev/null

cron、launchd、被其他工具當子行程呼叫的場景尤其該養成這個習慣——多打六個字元,換一個排程任務不會半夜卡死。

夾帶圖片:-i 跟 prompt 的先後順序,跟你在互動模式學的相反

第 4 章教過互動模式怎麼用 -i / --image 附圖,codex exec 一樣吃這個旗標——但有一個容易讓人跌一跤的地方:旗標跟 prompt 的先後順序,跟你剛練熟的互動模式手感是反的

互動模式你已經習慣寫成「旗標在前、prompt 在後」:

# 互動模式:-i 在前;圖片必須已做資料分級與遮罩
codex -i sanitized-bug.png "這張截圖哪裡有問題?只提出排查建議。"

codex exec 的位置引數解析邏輯,吃的是「prompt 先、-i 後」

# exec 模式:prompt 先、-i 後;圖片必須已做資料分級與遮罩
codex exec "這張截圖哪裡有問題?只提出排查建議。" -i sanitized-bug.png

順序寫反會發生什麼事

如果照互動模式的手感寫成 codex exec -i sanitized-bug.png "這張截圖哪裡有問題?",社群回報過會被誤判成「這次沒有位置引數、改讀 stdin」,結果就是卡住不動,或行為跟你預期的完全不一樣——症狀跟上一個「stdin 沒關好」的雷很像,但根因不一樣:一個是旗標順序寫反,一個是父行程沒關 stdin。兩種都認得,遇到「exec 莫名卡住」時才好判斷該往哪個方向查。

多張圖片一樣支援逗號分隔或重複旗標(跟互動模式語法相同)。哪些終端機貼不了原始影像、貼圖失敗時該怎麼排除,複習第 4 章即可,這裡不重複。

--ephemeral:不持久化 session rollout

平常 codex exec 跑完會把這段對話存成 session rollout 檔(歷程紀錄,放在 ~/.codex/sessions/,詳見第 7 章)。如果這次是「一次性、不需要後續 resume」的 CI 任務,加 --ephemeral 讓它不持久化 session rollout

codex exec --ephemeral "triage this repository and suggest next steps"

重要提醒

--ephemeral 不持久化可續接的 rollout,所以不能事後 resume。如果你打算做「先分析、再實作」的兩段式串接(下面就講),千萬別加 --ephemeral,否則第二步找不到第一步的 session。它也不等於「任何其他 log/telemetry 都不存在」。

codex exec resume:讓第二次接著第一次

codex exec resume 讓你沿用前一輪的 transcript(對話歷程)、計畫、核准設定繼續跑下一句指令。最經典的用法是「兩段式自動化」:第一段先讓它分析,第二段再叫它動手實作,而且它記得第一段的脈絡。

官方確認的兩種形式(逐字):

codex exec resume --last "now implement the fix you proposed"
codex exec resume <SESSION_ID> "continue with the next step"
  • --last:接續最近一次的對話。
  • <SESSION_ID>:指定某一個 session 的 ID 來接續。

重要提醒

0.139.0(2026-06-09)修了一個 bug:codex exec resume --last "..." 現在會正確地把尾隨的字串當成新的初始 prompt,而不是誤判成 session ID。所以請確認你的版本 ≥ 0.139.0,舊版這招可能不靈。(版本以實機 codex --version 為準。)

小技巧

還有一個 codex exec resume --all,官方 CLI 參考頁逐字解釋:「Include sessions outside the current working directory when selecting the most recent session.」——意思是挑「最近一次 session」時,連其他目錄的 session 也一起納入考慮(不限於你現在這個資料夾)。當你的排程任務跨多個專案目錄、想接續任意目錄最近那次時用得到。

多階段 resume,別中途換模型

resume 接續的不只是對話內容,還有 prompt 的快取狀態——前面輪次已經送過的內容,同一個模型再問一次可以吃「快取折扣」,省錢又省時間。如果在同一條 resume 鏈裡中途切換 --model,非官方實測觀察到快取命中率會明顯下滑(一份社群測試從八千多 token 的快取命中掉到剩三千多)。兩段式、三段式的自動化流程,盡量從頭到尾固定用同一個模型;真的要換模型,就當成開一段全新的對話,不要用 resume 接下去。

跑很長的自動化鏈,別把安全底線全押在 AGENTS.md

第 5 章第 13 章教你把長期規則寫進 AGENTS.md,平常這樣做完全正確。但社群回報過一種情況:同一個 session 拉得很長之後,AGENTS.md 裡的規則有時在對話後段套用得不如一開始穩定。對「一次性、當下講完就結束」的用法影響不大,但 codex exec resume 串起來的多階段自動化正好就是「拉得很長的 session」——如果某條規則是「絕對不能做 X」這種安全紅線,更保險的做法是把它直接寫進每一次 exec 的 prompt 本身,不要完全依賴 AGENTS.md 在長對話裡全程被穩定遵守。常駐規則放 AGENTS.md、當次安全紅線在 prompt 裡再講一次,雙重保險。

10.4 環境變數認證與 non-TTY 防呆

最後一節處理兩個自動化必踩的雷:怎麼在沒有人登入的環境給它鑰匙,以及怎麼讓它別卡在「等你按 Y」

CODEX_API_KEY:exec 專屬的鑰匙

在 CI 或排程環境裡,沒有人能坐下來跑 codex login 點按鈕。這時由 CI 的 secret 機制把 CODEX_API_KEY 注入單一、受信任的步驟;命令列本身不寫 API key,也不顯示它的值。注入完成後,該步驟只需要執行一般指令:

# CODEX_API_KEY 由 CI secret 在這一個步驟注入;不要在命令列填入值
codex exec --json "triage open bug reports"

在自己的電腦第一次使用時,請先走第 2 章的登入流程;不要為了省一步,把 API key 貼到終端機、腳本或聊天訊息裡。

這裡有一個非常重要、容易記錯的限制——官方逐字:

"CODEX_API_KEY is only supported in codex exec"

也就是說:CODEX_API_KEY 這個環境變數只有 codex exec 認得,你在互動模式 codex 裡設它是沒用的。

重要提醒

API key 等同密碼。永遠不要把它寫進命令列、腳本、.env、截圖、工單或 Git commit。要用 CI 的 secret 機制(例如 GitHub Actions 的 secrets)在需要的單一步驟注入,並避免把值印進 log。憑證安全的完整規矩請複習第 3 章

比「別寫死」更進一步:金鑰別跟不受信任的程式碼共用同一個 job

光用 CI 的 secret 機制注入還不夠細。官方明確警告:如果把 CODEX_API_KEY 設成整個 job 層級的環境變數,同一個 job 裡若還跑了「repo 受控的程式碼」(build script、測試、第三方 action),這些程式碼理論上都讀得到這把鑰匙。別人只要在你的 repo 開一個夾帶惡意程式碼的 PR,讓你的 CI 自動去跑 build/test,就有機會把金鑰讀走外流——這條攻擊路徑不需要打穿任何防護,金鑰本來就攤在同一個行程的環境變數裡,誰都讀得到。更穩妥的做法:金鑰所在的那個 step,盡量別跟未信任的程式碼共用同一個 job,或改用官方 openai/codex-action 內建的 proxy 機制(金鑰留在 action 內部,不直接暴露成環境變數)。完整的 GitHub Actions 佈署方式第 11 章會講。

小技巧

認證憑證放在 ~/.codex/auth.json,設定目錄由 CODEX_HOME 控制(預設 ~/.codex)。把 CI 機器上的登入「搬過去」,沿用 auth.json 而不是設 CODEX_API_KEY,屬於第 11 章團隊協作的範圍——但如果你正考慮這條路,先知道兩個限制:auth.json 裡的 token 大約每 8 天左右會自動刷新一次,跑完記得把刷新後的檔案存回去,不然下次又要重新登入;而且它只適合單機、序列化執行,不能讓好幾個 codex exec 平行跑同一份 auth.json(並行寫入互搶,狀態會亂)。多數 CI 場景 CODEX_API_KEY 還是比較省心的預設選擇,auth.json 這條路留給有特殊理由(例如只想用 ChatGPT 訂閱額度、不想另外申請 API key)的情境用。auth.json 本身等同密碼,別 commit、別貼進工單,完整警語複習第 3 章

non-TTY 防呆:別讓它卡在 [Y/n]

這是自動化最常見的地雷,務必看懂。

互動模式下,Codex 動手前可能會跳出「要不要執行這個指令?[Y/n]」讓你確認。但在 CI / 排程 / agent 這類「non-TTY」環境(non-TTY = 沒有真正的互動終端,沒有人能回應 [Y/n])裡,這個 prompt 沒有人會去按——結果就是任務卡死,或者更糟,某些工具會在 stdin 結束時直接採預設值(等於沒確認就硬跑)。

防呆不是只有一條路。你要先選擇:這個 job 是越界就失敗(fail-closed),還是允許 Auto-review 審查符合資格的越界。兩者都不依賴人工 [Y/n],但安全語意完全不同。

codex -a never exec --sandbox workspace-write "run the cleanup task"
  • -a never--ask-for-approval never):從頭到尾不產生 approval request;沙箱內照跑,仍需核准的越界直接失敗,也不會啟動 Auto-review。適合封閉、可預測的 fail-closed CI。
  • --sandbox:同時界定它「能動到哪」(三值 read-only / workspace-write / danger-full-access,雙軸權限心法詳見第 6 章)。

若 job 確實需要讓獨立 reviewer 判斷 eligible escalation,則保留 on-request,再設定 approvals_reviewer="auto_review"。本站把這條路封裝成安全 launcher;完整 interactive/exec 配方與權限邊界見第 7 章 7.6

兩個旗標的可選值速查:

旗標簡寫可選值
--ask-for-approval-auntrusted | on-request | never
--sandbox-sread-only / workspace-write / danger-full-access

寫錯位置,指令直接被拒:-a 要放在 exec 前面

上面那行指令的旗標順序不是隨便排的。-a / --ask-for-approval 是「全域旗標」,跟 codexcodex exec 共用同一層,寫法上要放在 exec 這個子指令前面——也就是 codex -a never exec ...,不是 codex exec -a never ...。順序寫反,輕則被當成不認得的旗標直接報錯,重則悄悄沒生效,你以為關掉了核准暫停,其實根本沒關,腳本照樣可能卡在等你按鍵。

相對地,--sandbox--json--output-schema-o--ephemeral 這些管「這次 exec 具體怎麼跑」的旗標,習慣上放在 exec 後面。一個簡單的經驗法則:偏「這次要不要問我」這類全域行為的旗標放前面,偏「exec 這次輸出長怎樣、可以動多少東西」的旗標放後面。⚠️ 全域旗標與子指令旗標的分界,實際仍以你版本的 codex --help / codex exec --help 為準,部分旗標官方兩邊都收,寫哪邊都通。

不要再說「non-TTY 的 on-request 一定等於 never

沒有 Auto-review 時,non-TTY 確實無法顯示新的人工核准,越界通常只能失敗;但設定 approvals_reviewer="auto_review" 後,eligible approval request 可以交給 reviewer agent。差別正在「有沒有產生 request、誰來 review」,所以 on-request + auto_reviewnever 不能視為同一個模式。

重要提醒

過去有一個 --full-auto 旗標,現已標為 deprecated(棄用)。官方非互動頁逐字:「Codex keeps codex exec --full-auto as a deprecated compatibility flag and prints a warning. Prefer the explicit --sandbox workspace-write flag in new scripts.」——它只保留為相容路徑、會印警告,新腳本請改用明確的 --sandbox workspace-write。CI 環境用上面的 --sandbox + --ask-for-approval 組合明確控制,別再投資 --full-auto

小技巧

0.140.0(2026-06-15)起,non-TTY 背景命令可以用 Ctrl-C 中斷,並保留它已有的輸出與退出狀態。換句話說,即使在腳本裡被打斷,它也不會把已經做出來的成果整個丟掉。

看到這裡,你可能會想:「乾脆用 --yolo 把核准跟沙箱都關掉,不是更省事?」——先踩煞車。--dangerously-bypass-approvals-and-sandbox(別名 --yolo)拆的不只是核准暫停,連檔案圍欄和網路圍欄一起拆,官方原話是只在外部已加固的環境內使用。codex exec 自動化真正要解決的是「不要卡在等你按鍵」,不是「乾脆什麼都別擋」——上面 -a never + --sandbox workspace-write 的組合已經解決了卡住的問題,同時還留著沙箱圍欄保護你。--yolo 這條紅線的完整風險,第 6 章已經講透,這裡只提醒一句:能用 -a never + --sandbox 解決的自動化,就不要跳去 --yolo

非 git 目錄要加 --skip-git-repo-check

codex exec 預設會檢查你是不是在一個 Git 儲存庫裡(這是安全機制,提醒你「改檔前最好有版本控制當安全網」)。如果你故意要在一個非 git 的資料夾跑(例如純粹整理一批檔案),這個檢查會擋你,這時加上:

codex exec --skip-git-repo-check "organize these notes into folders"

小技巧

還有幾個 exec 專屬旗標在進階自動化會用到:--ignore-user-config(跳過 $CODEX_HOME/config.toml,讓這次跑乾淨、不受個人設定干擾)、--ignore-rules(跳過 execpolicy 的 .rules 規則檔)。日常用不到,知道它們存在即可,細節以 codex exec --help 為準。

10.5 🎓 高手進階

前面四節你已經會把 codex exec 接進腳本。這一節是寫給要把它放進正式 CI、跨夜排程、團隊流水線的人——新手可以先跳過,等你真的要「無人值守、出事還能查」時再回來。

小技巧

下面所有逐字旗標都來自官方 CLI 參考頁(reference),它才是旗標全集;非互動頁(noninteractive)是敘事介紹,沒列全。所以有些旗標你在非互動頁找不到,去 reference 頁查就有。

進階入口一:幾個你之前沒見過、但 CI 很需要的旗標

把前面零散提到的補齊成一張速查,全是官方逐字佐證的:

旗標 / 別名官方逐字描述用途
codex ecodex exec 短別名少打四個字
--json / --experimental-jsonbool"Print newline-delimited JSON events instead of formatted text."事件流(--experimental-json 是同義別名)
--ephemeralbool"Run without persisting session rollout files to disk."一次性任務不留歷程
--full-autobooldeprecated 相容路徑、會印 warning別用,改 --sandbox workspace-write
exec resume --lastbool"Resume the most recent conversation from the current working directory."接續本目錄最近一次
exec resume --allbool"Include sessions outside the current working directory when selecting the most recent session."跨目錄挑最近一次
--coloralways|never|auto"Control ANSI color in stdout."CI log 關掉色碼跳脫字元
CODEX_API_KEYkey環境變數認證, codex exec 認得由 CI secret 在單一步驟注入;命令列不寫值

小技巧

CI 的 log 檔常被 ANSI 色碼污染成一堆 \033[…m 亂碼。加 --color never 讓 stdout 不帶顏色,log 就乾淨可讀。

進階入口二:--json 事件流的 schema 與 jq 解析鐵律

10.2 節教過 --json 會吐逐行 JSON 事件(JSONL)。要在 CI 裡穩穩解析,先分清楚兩層:

頂層事件型別(官方逐字列舉,可信):thread.startedturn.startedturn.completedturn.faileditem.*error

每個 item.* 裡面包一個 item 物件,有三個相位:item.started →(item.updated)→ item.completed,每個 item 都帶 item.iditem.type。官方逐字列出的 item.type 種類有:agent 訊息、推理、指令執行、檔案修改、MCP 工具呼叫、網路搜尋、計畫更新等。

⚠️ 關鍵分界:型別名是官方的(可信),但 item 物件「裡面有哪些欄位」官方只公開了型別名加兩個範例,完整欄位細節目前最齊的來源是第三方 cheatsheet。 下表的欄位名請當「參考、待實機驗」,別寫死:

item.type欄位(⚠️ 欄位名待實機驗)
agent_messagetext
command_executioncommandaggregated_outputexit_codestatus
file_changechanges[]:每筆 {path, kind}kindadd/delete/update
mcp_tool_callservertoolargumentsresulterrorstatus

幾個立刻能用的 jq 配方(jq 是處理 JSON 的小工具,第 9 章提過):

🍎 Mac / 🐧 Linux

# 1) 不靠 -o,直接從事件流撈「最終訊息」
codex exec --json "summarize repo risks" \
  | jq -r 'select(.type=="item.completed" and .item.type=="agent_message") | .item.text'

# 2) 撈 token 用量(成本監控)
codex exec --json "do task" \
  | jq -c 'select(.type=="turn.completed") | .usage'

# 3) 撈它實際跑過的每一條命令(稽核)
codex exec --json "..." \
  | jq -c 'select(.type=="item.completed" and .item.type=="command_execution")
           | {cmd:.item.command, exit:.item.exit_code, status:.item.status}'

🪟 Windows(PowerShell)

codex exec --json "summarize repo risks" | jq -r 'select(.type==\"item.completed\" and .item.type==\"agent_message\") | .item.text'

解析鐵律(三條,務必照做)

  1. 型別名信官方,欄位名先採樣——寫 filter 前先跑一次 codex exec --json "noop" | jq -c '.type' 在你的版本上看真實長相。
  2. 未知型別一律略過、不報錯——Codex 數天就出一版,schema 沒凍結,parser 要容錯,碰到沒見過的 type 跳過就好。
  3. --json 事件流 ≠ 磁碟上的 session 檔——~/.codex/sessions/ 裡的 rollout 檔是另一套形狀(用 role/tool_call 那類欄位),別把同一個 jq filter 套兩邊。

小技巧

turn.completedusageinput_tokens / cached_input_tokens / output_tokens / reasoning_output_tokens 四欄。CI 監控成本時記得把 cached_input_tokens(走快取折扣)和一般 input_tokens 分開計,不然成本會算不準。(實際計價以 OpenAI 官方定價頁為準,本書不報價。)

進階入口三:CI 別只信退出碼——三重判斷

10.1 節說過「退出碼是粗閘,別只信它」。高手在 CI 裡用三重判斷才算數,任一條不過就當失敗:

  1. 退出碼$? 非零 → 失敗(官方確認:提交失敗會非零、必要 MCP 啟動失敗會以錯誤退出、git apply 衝突也會非零)。
  2. 失敗事件--json 串流裡出現 turn.failederror 事件 → 失敗(比退出碼更細,有時退出碼是 0 但中途其實出過事)。
  3. 產出物非空:它「該生出來的東西」(修補檔 / schema 輸出檔)真的存在、而且非空,才算成功。

第二條的 jq 寫法:

🍎 Mac / 🐧 Linux

out=$(codex exec --json "...")
echo "$out" | jq -e 'select(.type=="turn.failed" or .type=="error")' >/dev/null \
  && { echo "Codex reported a failure event"; exit 1; }

小技巧

第三條的精神,就是官方 CI 自動修復範例的做法:它不單靠退出碼,而是用 if [ -s codex.patch ](檔案存在且非空)判斷有沒有真的產出修補檔。完整的 GitHub Actions 自動修復、cron / launchd 排程那一整套生產線流程,我們下放到第 14 章專章講,這裡先把 exec 本身的判斷心法立好。

進階入口四:--output-schema + MCP 同開的「靜默降級」陷阱 ⚠️

這是高手級 CI 才會踩到、但一踩就很難 debug 的雷,務必記住。

  • 觸發條件:你同時 --json--output-schema(要求最終訊息照固定 schema),而且這次跑有掛 MCP server / tools。
  • 症狀--output-schema 那層嚴格格式被靜默丟棄,最終訊息退回成沒結構的自由文字——可能缺最外層 {}、屬性間漏逗號、key 沒加引號,甚至整段被 markdown code block 包起來。你的下游解析會莫名其妙爆掉,卻找不到錯在哪。

重要提醒

這個行為來自社群回報(GitHub issue #15451,狀態已 Closed),確認版本是 codex-cli 0.116.0;是否在新版已修尚待確認,請以實機測試為準。但「拿到 schema 輸出一定要機械驗證」這條防呆,不管修沒修都該做。

三條規避鐵律:

  1. 拆兩段:需要嚴格 schema 輸出的步驟,盡量別同時掛 MCP / tools。把「需要 MCP 的分析」和「結構化輸出」拆開——前段用 MCP 分析、後段 resume 接續但只跑「無工具的彙整」來產 schema 輸出。
  2. jq empty 驗合法性:拿到 -o 或 schema 輸出檔後,一定機械驗一次:

    🍎 Mac / 🐧 Linux

    jq empty ./output.json && echo "OK: valid JSON" || { echo "FAIL: not valid JSON"; exit 1; }
    jq empty 會把檔讀一遍但不輸出,只要 JSON 不合法就非零退出。FAIL 就 retry 或報錯,別盲信
  3. 鎖版本:Codex 數天一版、事件 schema 沒凍結。CI 裡把 Codex 版本釘死(例如本機 cron 用 npm i -g @openai/codex@<版本> 釘版),避免哪天 schema 漂移就把你的 parser 整個打爛。

小技巧

「鎖版本 + 採樣 + 容錯 parser」是吃這口飯的三件套。任何把 --json 事件接進長期自動化的人,版本一漂、欄位一改,沒鎖版的腳本就會在某個半夜默默壞掉,而且因為是 non-TTY 沒人看著,你隔天才發現。

本章小結

這一章你學會了把 Codex 從「對話夥伴」變成「自動化工人」:

  • codex exec 非互動跑、結果走 stdout、用退出碼判斷成敗(但別只信退出碼,搭配「產出物在不在」雙重判斷)。
  • 結構化輸出三招:--json(看過程的事件流)、--output-schema(固定最終欄位)、-o(順手存檔)。
  • 串接:先把指示與已遮罩資料組成一份完整 prompt,再用管道和 - 從 stdin 讀入;也能用 codex exec resume 接續記憶(但 --ephemeral 不能 resume)。
  • 自動化防呆CODEX_API_KEY 僅由 CI secret 在受信任的單一步驟注入(僅 exec 認得,命令列不寫值);封閉 CI 用 --ask-for-approval never 讓越界 fail-closed,需要自動審查 eligible escalation 則用 on-request + auto_review;非 git 目錄才考慮 --skip-git-repo-check
  • 高手進階:CI 別只信退出碼,疊上「失敗事件 + 產出物非空」三重判斷;--json 事件「型別名信官方、欄位先採樣、未知略過、鎖版本」;--output-schema + MCP 同開有靜默降級陷阱,拿到輸出一定 jq empty 驗。

下一章我們會繼續把 exec 接到更大的場景;而 GitHub Actions 自動審 PR、cron / launchd 跨夜排程那一整套生產線流程,留到第 14 章專章講透。

動手試試

  1. codex exec "summarize the repository structure",看它印什麼;再跑一次 echo $?(PowerShell 用 echo $LASTEXITCODE)看退出碼。
  2. codex exec --json "list the top 3 files in this repo" | jq,觀察 JSONL 事件流,找找看有沒有 turn.completed 那一行的 token 用量。
  3. 把一句不含密鑰、個資或原始日誌的長指令存進 prompt.txt,用 codex exec - < prompt.txt 餵進去,體會「整段 prompt 從檔案來」的感覺。
  4. (進階)跑 codex exec --json "list files" | jq -c 'select(.type=="turn.failed" or .type=="error")',正常情況應該沒有任何輸出——這就是 CI 用「失敗事件」當第二道閘門的偵測寫法。