아빠노트

codex exec 결과를 파일로 받기 — -o와 --json 윈도우 실측 (Codex CLI)

실측 2026-09-17처음 쓴 날 2026-09-17 · English version

codex exec의 마지막 답만 필요하면 -o 파일, 진행 과정까지 기계로 읽으려면 --json > 파일을 씁니다. 둘은 같이 쓸 수 있습니다. 이번 실험에서는 -o에 없는 폴더를 주자 파일 저장에 실패했는데 종료 코드는 0이었으니, 저장 결과를 따로 확인하는 편이 좋습니다.

한 줄로

codex exec의 마지막 답만 필요하면 -o 파일, 진행 과정까지 기계로 읽으려면 --json > 파일을 씁니다. 둘은 같이 쓸 수 있습니다. 이번 실험에서는 -o에 없는 폴더를 주자 파일 저장에 실패했는데 종료 코드는 0이었으니, 저장 결과를 따로 확인하는 편이 좋습니다.

이런 분께

  • 스크립트나 AI 에이전트에서 codex exec를 부르고 답을 파일로 넘겨받고 싶은 분
  • 화면에 찍히는 머리말(OpenAI Codex v..., tokens used)을 빼고 답만 남기고 싶은 분
  • --json 출력이 어떤 모양인지 먼저 보고 싶은 분

실측 환경

항목값
OSWindows 11 Home (10.0.26200)
셸Git Bash, PowerShell 7.6.6
codexcodex-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,707

last.txt는 2바이트였습니다. 끝에 줄바꿈이 없어서 wc -l로 세면 0줄로 나옵니다.

0 2 last.txt
--- cat -A ---
OK

cat -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("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)}'
OK

3. 둘을 같이 쓰기

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 4

PowerShell 회차 파일에서만 CR 문자가 4개 나왔고, 크기도 Git Bash 쪽보다 4바이트 컸습니다. 이 기록만으로는 PowerShell의 >가 줄바꿈을 바꿨는지까지는 가릴 수 없습니다. 어쨌든 JSONL을 읽는 쪽이 \n으로만 자른다면 줄 끝에 \r이 남으니, 위 Node 예시처럼 /\r?\n/로 자르는 편이 안전합니다. -o 파일은 두 셸 모두 2바이트로 같았습니다.

결과

회차셸옵션종료 코드걸린 시간만들어진 파일
1Git Bash-o last.txt08.4초last.txt 2바이트, 0줄(줄바꿈 없음)
2Git Bash--json > events.jsonl06.1초4줄, 345바이트
3Git Bash--json -o 함께08.4초2바이트 + 4줄 345바이트
4Git Bash-o nodir/last.txt (없는 폴더)06.1초없음
5PowerShell 7--json -o 함께08.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). 종료 코드만으로는 파일 저장 성공을 알 수 없습니다. 기존 제안의 test -s ... || echo ...도 메시지만 찍고 0으로 끝날 수 있고, 이전 실행의 파일이 남아 있으면 잘못 통과합니다.

이번에 새로 확인함 — 2026-09-28

완전한 Bash 예제 verify-new-output.sh를 현재 폴더에 저장하고 실행합니다. Bash·mktemp·cmp와 설치 및 로그인된 Codex가 필요합니다.

bash verify-new-output.sh output

스크립트는 mktemp -d ./codex-check.XXXXXXXX로 매번 새 폴더를 만들고 그 안에서 -o last.txt로 OK를 요청합니다. Codex의 종료 코드가 0이 아니면 그 값을 그대로 반환합니다. 0이어도 새 last.txt가 일반 파일인지(심볼릭 링크 제외), 내용이 줄바꿈 없는 정확한 2바이트 OK인지 cmp로 확인하고, 다르면 1로 끝납니다. 성공할 때만 verified: <새 폴더>/last.txt를 출력합니다. 검사한 폴더는 확인용으로 남기며, 기존 파일을 덮어쓰거나 삭제하지 않습니다. 일반 질문에 적용할 때는 OK 대신 그 작업의 내용·형식 검사를 정해야 합니다.

이번 확인은 Codex 실측이 아니라 셸 검사기의 모의 시험입니다. Linux의 Bash 5.2.21에서 codex 자리에 시험용 프로그램을 넣었습니다. 정상 내용은 0, 파일 없음·빈 파일·틀린 내용·추가 줄바꿈·심볼릭 링크는 각각 1, 올바른 파일을 만들고도 생산자가 37로 끝나는 경우는 37이었습니다. 부모 폴더에 예전 OK 파일을 둔 상태에서도 누락 사례는 실패했고, 각 실행의 새 폴더가 서로 다름을 확인했습니다. 이 환경에는 Codex 실행 파일·로그인·Windows가 없어 실제 -o 동작은 다시 시험하지 않았습니다. 위 2026-09-17 결과는 그대로 보존했습니다.

확인하지 않은 것

  • 답이 여러 줄이거나 한국어일 때 -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)의 숫자가 왜 다른지는 따지지 않았습니다. 두 값이 같은 기준인지 알 수 없습니다.

함께 보기

2026-09-28 수정: 2절 답 추출 코드가 3절에서야 생기는 both_events.jsonl을 읽고 있어, 2절에서 만든 events.jsonl을 읽도록 고쳤습니다.