DEBUG.WIKI
京东云;让 AI 进入真实业务;零售 · 物流 · 客服 · 硬件;查看方案 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国
Evoxt;最高 6.0GHz CPU;高频云服务器 · 免费每周备份;查看配置 →

curl 加上 --write-out 后,JSON 解析报 Extra data

将curl状态文本从响应正文中分离,用stderr输出切换保持JSON管道纯净,并分别核对传输、解析与HTTP状态。

接口正文能单独解析成 JSON,加上 curl -w '\n%{http_code}\n' 再传给 python3 -m json.tool,却出现 Extra data。这通常是 curl 在正文后追加了状态码,管道里已经包含两个独立的数据片段。

例如正文为 {"ok":true},后面又追加一行 200。即使两段之间有换行,也不是一个完整的单个 JSON 文档。不要据此修改服务端序列化,更不要忽略解析器错误后继续使用部分结果。

把状态文本改写到标准错误

curl 的 --write-out 默认在传输结束后向标准输出写入指定信息。它不属于响应正文,但默认与正文共用输出流。curl 7.63.0 及以后支持 %{stderr},可以只改变后续 write-out 文本的去向:

curl --silent --show-error \
  --write-out '%{stderr}HTTP %{http_code}\n' \
  'https://api.example.com/status' |
  python3 -m json.tool

URL 为占位值,替换为允许访问且预期返回 JSON 的接口。这里正文仍进入管道,HTTP 200 一类状态文本写到标准错误,不会交给 JSON 解析器。--silent --show-error 隐去进度条并保留 curl 的失败信息;静默模式本身不会取消 --write-out。

不要在 curl 后补 2>&1,也不要用会合并标准输出和标准错误的包装器运行,否则状态文本和错误信息又会混入 JSON。若只想保留 curl 的诊断,可把 curl 的标准错误单独写到受保护日志文件;日志可能含接口地址或认证相关信息,分享前应检查。

先用 curl --version 核对版本。早于 7.63.0 的 curl 不支持这个输出切换变量,需要升级到适用版本,或沿用项目已有的“正文写文件、状态单独读取”流程。不要把 unknown --write-out variable 当成 HTTP 服务端错误。

解析成功和请求成功分别检查

管道最后一段的成功状态不能代表 curl 已成功。下面是 Bash 专用的检查示例,使用 PIPESTATUS 在管道结束后立刻保存两段退出码;不要在保存前插入其他命令:

curl --silent --show-error \
  --write-out '%{stderr}HTTP %{http_code}\n' \
  'https://api.example.com/status' |
  python3 -m json.tool
pipeline_codes=("${PIPESTATUS[@]}")
printf 'curl=%s json=%s\n' "${pipeline_codes[0]}" "${pipeline_codes[1]}"

这段写法用于没有提前退出管道检查的 Bash 环境;脚本若开启了 set -e、pipefail 或错误处理 trap,应把检查放入项目已有的错误处理结构,避免还没保存状态就退出。它不是 POSIX sh 或 zsh 的通用写法。

curl 非零时,按其实际传输错误处理;解析器非零时,检查接口是否返回 HTML、空正文、截断 JSON 或其他格式。即使两段都为零,还应按接口契约判断 HTTP 状态码:默认 curl 不会仅因为收到 HTTP 404 就返回非零,而某些错误响应本身也可以是合法 JSON。

write-out 在传输失败时也可能输出,因此看到一行状态信息不证明正文完整。确认分流后,解析器应该只收到服务端正文;若仍报 Extra data,再检查响应本身是否包含多个 JSON 值或额外文本。

本次修改只涉及调用参数和输出流。撤回时恢复原脚本,但不要重新把状态文本接回需要单个 JSON 文档的管道。保留诊断日志与业务结果的独立去向,能避免同类问题再次出现。