0

jq解析curl -w输出格式化JSON数据实战:5个步骤把性能数据变可编程JSON

2026.06.15 | youres | 7次围观

日常用 curl 做网站巡检时,-w 参数输出的性能数据往往是多行字符串,读起来费劲,交给程序处理更是一堆字符串切割的脏活累活。实际上只需要加一个 jq,就能把原本杂乱的输出变成干净的 JSON,程序解析省心,导出报告也方便。这篇文章就来讲讲怎么把 curl -w 的输出交由 jq 处理成结构化 JSON。

一、curl -w 输出到底是什么

curl 的 -w(--write-out)参数用于在请求结束后,按照指定格式输出统计指标。常见变量有:

  • time_namelookup — DNS 解析耗时
  • time_connect — TCP 连接建立耗时
  • time_starttransfer — 首字节到达耗时(TTFB)
  • time_total — 请求总耗时
  • http_code — HTTP 状态码
  • url_effective — 最终请求 URL
  • num_redirects — 重定向次数

默认输出是一行键值对字符串,形如 "key1=value1 key2=value2 ..."。同时输出多个变量时,看起来是这样:

time_namelookup: 0.012
time_connect: 0.034
time_starttransfer: 0.089
time_total: 0.145
http_code: 200

这种格式用眼睛看还算直观,但交给程序处理就得写正则解析。换个写法,让 curl 直接输出 JSON 格式,再用 jq 提取,就干净多了。

二、让 curl -w 直接输出 JSON

curl 支持用 %{json} 格式符直接将所有指标以 JSON 格式输出。用法很简单:

curl -s -o /dev/null -w "%{json}" https://www.youres.cn

输出示例:

{"url":"https://www.youres.cn","http_code":200,"time_namelookup":0.005,"time_connect":0.021,"time_starttransfer":0.065,"time_total":0.082,"num_redirects":0}

这是一行标准 JSON 字符串。但需要注意 curl 默认输出的 JSON 不是标准 RFC 8259 格式,某些特殊字符未做转义,直接丢给 jq 有时会报错。更稳妥的做法是自己在 -w 格式字符串里构造 JSON。

三、用 jq 格式化 curl -w 输出

标准做法是让 curl 输出键值对,然后通过 jq 重新组装成标准 JSON。核心思路分两步:

第一步:用 \\n 分隔每个指标,输出形如 "key=value" 的多行

curl -s -o /dev/null -w "time_namelookup=%{time_namelookup}\\ntime_connect=%{time_connect}\\ntime_starttransfer=%{time_starttransfer}\\ntime_total=%{time_total}\\nhttp_code=%{http_code}\\n" https://www.youres.cn

输出:

time_namelookup=0.005
time_connect=0.021
time_starttransfer=0.065
time_total=0.082
http_code=200

第二步:交给 jq 读取每行,用 split("=") 提取键值,重组成 JSON 对象

... | jq -Rs "split(\"\\n\") | map(select(length > 0)) | map(split(\"=\")) | map({\"key\": .[0], \"value\": .[1]}) | from_entries"

输出:

{
  "time_namelookup": "0.005",
  "time_connect": "0.021",
  "time_starttransfer": "0.065",
  "time_total": "0.082",
  "http_code": "200"
}

到这里就已经得到标准 JSON 了。jq 的 -R 参数读取原始字符串(不当作 JSON 解析),-s 把多行合并为一个数组,from_entries 把键值对数组直接转成 JSON 对象。

数值型指标默认是字符串,可以在 jq 里加一步类型转换:

... | jq "to_entries | map({key, value: (.value | tonumber? // .value)}) | from_entries"

四、批量检测时配合 jq 生成 JSON 数组

做网站批量巡检时,常见的做法是把每台服务器的结果追加到日志文件。用 jq 配合 awk 可以在追加时直接输出 JSON 数组,方便后续导入数据库或可视化工具。

#!/bin/bash
DOMAINS=("www.youres.cn" "example.com" "test.org")
RESULTS=()

for domain in "${DOMAINS[@]}"; do
  DATA=$(curl -s -o /dev/null -w "domain=$domain\\ntime_total=%{time_total}\\nhttp_code=%{http_code}\\n" "https://$domain" | jq -Rs "split(\"\\n\") | map(select(length > 0)) | map(split(\"=\")) | map({\"key\": .[0], \"value\": .[1]}) | from_entries")
  RESULTS+=("$DATA")
done

printf "%s\\n" "${RESULTS[@]}" | jq -s "."

jq -s "." 把每行 JSON 对象合并成一个 JSON 数组,最终输出:

[
  {"domain": "www.youres.cn", "time_total": "0.082", "http_code": "200"},
  {"domain": "example.com", "time_total": "0.231", "http_code": "301"},
  {"domain": "test.org", "time_total": "1.203", "http_code": "0"}
]

这个数组可以直接 jq ".[] | select(.http_code | tonumber > 399)" 筛选异常,也可以 jq -c ".[] | {domain, time_total}" 只取关键字段。

五、把 JSON 数据导出为 CSV 格式

很多运维同学习惯用 Excel 看巡检数据。用 jq 的 @csv 输出器可以直接导出 CSV,Excel 打开即用:

curl -s -o /dev/null -w "domain=$domain\\ntime_total=%{time_total}\\nhttp_code=%{http_code}\\n" "https://$domain" | jq -Rs "split(\"\\n\") | map(select(length > 0)) | map(split(\"=\")) | map({\"key\": .[0], \"value\": .[1]}) | from_entries | [.domain, .http_code, .time_total] | @csv"

输出直接是标准 CSV 格式,保存到文件后无需额外转换。

六、常见问题排查

问题一:jq 报错 "invalid JSON"

curl -w 输出的值里如果包含引号或特殊字符,会破坏 JSON 格式。解决方案是让 jq 用 from_entries 自动处理转义,不要手动拼字符串。

问题二:某个指标为空

某些情况下 curl 的某个时间变量可能为空值,导致 split 后出现空键。用 map(select(. != "")) 在转 JSON 前过滤空行。

问题三:批量时内存占用高

用 jq -s 读取全部结果再合并,文件过大会吃内存。建议改用 jq -c 每行单独输出,然后在 Python 或其他脚本里做流式追加。

七、总结

curl -w 配合 jq 的核心价值在于把散乱的文本输出变成结构化数据。思路其实很直接:先用固定分隔符把 curl 输出整理成键值对文本,再用 jq 解析重组为标准 JSON。掌握这个套路之后,不管是一次性检测还是定时巡检,都可以直接输出 JSON 数组,数据流转后续的告警、可视化、数据库入库都能顺畅对接。

日常运维中推荐把这个套路封装成 Shell 函数:

function curl_json() {
  local url=$1
  curl -s -o /dev/null -w "url=${url}\\nhttp_code=%{http_code}\\ntime_total=%{time_total}\\ntime_namelookup=%{time_namelookup}\\ntime_connect=%{time_connect}\\ntime_starttransfer=%{time_starttransfer}\\n" "$url" | \\
    jq -Rs "split(\"\\n\") | map(select(length > 0)) | map(split(\"=\")) | map({\"key\": .[0], \"value\": .[1]}) | from_entries"
}
# 用法
curl_json "https://example.com" | jq ".http_code"

一行命令搞定结构化输出,curl 和 jq 各司其职,简单实用。

相关文章:

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论