AI 해결 노트 · 2026-09-17 · 실측 2026-09-17
codex exec 결과를 파일로 받기 — -o와 --json 윈도우 실측 (Codex CLI)
한 줄로
codex exec의 마지막 답만 필요하면 -o 파일, 진행 과정까지 기계로 읽으려면 --json > 파일을 씁니다. 둘은 같이 쓸 수 있습니다. 이번 실험에서는 -o에 없는 폴더를 주자 파일 저장에 실패했는데 종료 코드는 0이었으니, 저장 결과를 따로 확인하는 편이 좋습니다.
이런 분께
- 스크립트나 AI 에이전트에서
codex exec를 부르고 답을 파일로 넘겨받고 싶은 분 - 화면에 찍히는 머리말(
OpenAI Codex v...,tokens used)을 빼고 답만 남기고 싶은 분 --json출력이 어떤 모양인지 먼저 보고 싶은 분
실측 환경
| 항목 | 값 |
|---|---|
| OS | Windows 11 Home (10.0.26200) |
| 셸 | Git Bash, PowerShell 7.6.6 |
| codex | codex-cli 0.153.4 |
| 모델(실행 머리말 표시) | gpt-6-astra |
| 질문 | Reply with the single word OK. (모든 회차 같음) |
| 공통 옵션 | -s read-only --skip-git-repo-check (Git Bash 1~4회차는 < /dev/null로 stdin을 닫음) |
이 글에는 stdin을 닫지 않았을 때의 비교가 없습니다. 에이전트 셸처럼 stdin이 파이프로 열려 있을 때 codex exec가 끝나지 않던 실측은 codex exec가 stdin 대기에서 안 끝날 때에 따로 적었습니다.
도움말에 있는 옵션
codex exec --help에서 이 글과 관련된 두 줄입니다.
--json
Print events to stdout as JSONL
-o, --output-last-message <FILE>
Specifies file where the last message from the agent should be written--json은 파일 옵션이 아닙니다. 표준출력으로 내보내므로 파일로 받으려면 >로 돌려야 합니다.
1. -o로 마지막 답만 받기
codex exec -s read-only --skip-git-repo-check -o last.txt "Reply with the single word OK." < /dev/null종료 코드 0, 8.4초 걸렸습니다. 출력이 두 갈래로 나뉩니다. 표준출력에는 답 OK 한 줄만 나왔고, 머리말과 진행 기록은 모두 표준오류로 갔습니다.
Reading additional input from stdin...
OpenAI Codex v0.153.4
--------
workdir: C:\Users\User\AppData\Local\Temp\longtail_A\exec
model: gpt-6-astra
provider: openai
approval: never
sandbox: read-only
...
codex
OK
tokens used
6,707last.txt는 2바이트였습니다. 끝에 줄바꿈이 없어서 wc -l로 세면 0줄로 나옵니다.
0 2 last.txt
--- cat -A ---
OKcat -A로 봐도 줄 끝 표시($)가 없습니다. 파일을 다른 파일에 이어 붙이거나 줄 수로 성공을 판정하는 스크립트라면 이 점을 감안해야 합니다.
2. --json으로 이벤트 전체 받기
codex exec -s read-only --skip-git-repo-check --json "Reply with the single word OK." < /dev/null > events.jsonl종료 코드 0, 6.1초. 표준오류에는 Reading additional input from stdin... 한 줄만 남았고 머리말은 나오지 않았습니다. 파일은 4줄, 345바이트였습니다.
{"type":"thread.started","thread_id":"01a0ae78-dd26-7ce2-8b79-6da03f94ac20"}
{"type":"turn.started"}
{"type":"item.completed","item":{"id":"item_0","type":"agent_message","text":"OK"}}
{"type":"turn.completed","usage":{"input_tokens":18453,"cached_input_tokens":12416,"cache_write_input_tokens":0,"output_tokens":5,"reasoning_output_tokens":0}}답은 type이 item.completed이고 item.type이 agent_message인 줄의 text에 있습니다. 토큰 사용량은 마지막 turn.completed 줄의 usage에 들어 있습니다.
실행한 셸에서 command -v jq가 경로를 출력하지 않아, Node로 답만 뽑았습니다.
node -e 'const fs=require("fs");for(const l of fs.readFileSync("both_events.jsonl","utf8").split(/\r?\n/)){if(!l)continue;const e=JSON.parse(l);if(e.type==="item.completed"&&e.item.type==="agent_message")console.log(e.item.text)}'OK3. 둘을 같이 쓰기
codex exec -s read-only --skip-git-repo-check --json -o both_last.txt "Reply with the single word OK." < /dev/null > both_events.jsonl종료 코드 0. both_last.txt는 2바이트 OK, both_events.jsonl은 4줄 345바이트로, 1·2절과 같은 모양이 한 번에 나왔습니다.
4. PowerShell 7에서
codex exec -s read-only --skip-git-repo-check --json -o ps_last.txt "Reply with the single word OK." > ps_events.jsonl
"codex exit=$LASTEXITCODE"
Get-Content ps_events.jsonl | ForEach-Object { $_ | ConvertFrom-Json } |
Where-Object { $_.type -eq 'item.completed' } | ForEach-Object { $_.item.text }이 스크립트는 pwsh -NoProfile -File로 돌렸습니다. 바깥에서 stdin이 어떤 상태였는지는 기록하지 않았습니다. codex exit=0, ps_last.txt 2바이트, ps_events.jsonl 4줄에 OK가 뽑혔습니다. 파일 첫 3바이트는 7B 22 74({"t)로 BOM이 붙지 않았습니다.
크기는 달랐습니다. 같은 4줄인데 PowerShell 쪽은 349바이트였습니다. CR 문자를 세어 봤습니다.
CR count:
events.jsonl 0
ps_events.jsonl 4PowerShell 회차 파일에서만 CR 문자가 4개 나왔고, 크기도 Git Bash 쪽보다 4바이트 컸습니다. 이 기록만으로는 PowerShell의 >가 줄바꿈을 바꿨는지까지는 가릴 수 없습니다. 어쨌든 JSONL을 읽는 쪽이 \n으로만 자른다면 줄 끝에 \r이 남으니, 위 Node 예시처럼 /\r?\n/로 자르는 편이 안전합니다. -o 파일은 두 셸 모두 2바이트로 같았습니다.
결과
| 회차 | 셸 | 옵션 | 종료 코드 | 걸린 시간 | 만들어진 파일 |
|---|---|---|---|---|---|
| 1 | Git Bash | -o last.txt | 0 | 8.4초 | last.txt 2바이트, 0줄(줄바꿈 없음) |
| 2 | Git Bash | --json > events.jsonl | 0 | 6.1초 | 4줄, 345바이트 |
| 3 | Git Bash | --json -o 함께 | 0 | 8.4초 | 2바이트 + 4줄 345바이트 |
| 4 | Git Bash | -o nodir/last.txt (없는 폴더) | 0 | 6.1초 | 없음 |
| 5 | PowerShell 7 | --json -o 함께 | 0 | 8.3초 | 2바이트 + 4줄 349바이트(CR 4개) |
실패한 것
4회차가 이 글에서 가장 조심할 부분입니다. -o에 존재하지 않는 폴더를 줬더니 답은 표준출력에 OK로 나왔지만 파일 쓰기는 실패했습니다. 표준오류에만 이 줄이 남았습니다.
Failed to write last message file "nodir/last.txt": 지정된 경로를 찾을 수 없습니다. (os error 3)그런데 codex의 종료 코드는 0이었습니다(codex exit=0). 종료 코드만 보고 성공으로 처리하는 스크립트는 빈손으로 넘어갑니다. 폴더를 미리 만들고, 실행 뒤 파일이 생겼는지 확인하는 줄을 넣는 편이 낫습니다.
mkdir -p out
codex exec -s read-only --skip-git-repo-check -o out/last.txt "질문" < /dev/null
test -s out/last.txt || echo "last message file missing"(위 세 줄 묶음 자체는 돌리지 않았고, 4회차 결과를 보고 붙인 제안입니다.)
확인하지 않은 것
- 답이 여러 줄이거나 한국어일 때
-o파일의 줄바꿈·인코딩은 재지 않았습니다. 이번 답은OK한 단어뿐입니다. - codex가 명령을 실행하는 긴 작업에서
--json에 어떤 이벤트 종류가 더 나오는지는 보지 않았습니다. 이번에 본 종류는thread.started,turn.started,item.completed,turn.completed넷입니다. --output-schema(답의 JSON 모양 지정)는 도움말에만 있고 돌려 보지 않았습니다.-o파일이 이미 있을 때 덮어쓰는지 이어 쓰는지는 확인하지 않았습니다.- 명령 프롬프트(cmd.exe)의
>와 맥·리눅스는 확인하지 않았습니다. - 1회차(
tokens used 6,707)와 2회차(input_tokens 18453)의 숫자가 왜 다른지는 따지지 않았습니다. 두 값이 같은 기준인지 알 수 없습니다.