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 文档的管道。保留诊断日志与业务结果的独立去向,能避免同类问题再次出现。


