
curl --write-out 完全指南用 -w 精确输出传输统计、HTTP 头与时间数据【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl--write-out短选项-w是 curl 命令行工具在每次传输完成后按自定义格式向标准输出打印统计信息的核心机制。它内置了 70 余个%{variable}变量涵盖 HTTP 状态码、传输字节数、耗时、TLS 证书、URL 解析结果等并支持%header{name}、%output{file}、%time{format}等进阶指令是编写监控脚本、自动化测试与日志采集时的标准利器。读完本文你将掌握 write-out 的全部变量语义、转义规则、输出重定向技巧以及它在 curl 源码中的底层实现原理。本文内容以 docs/cmdline-opts/write-out.md 为骨架结合 curl 仓库中 src/tool_writeout.c、src/tool_writeout_json.c 等源码与 tests/data 下的测试用例进行纵深讲解。一、--write-out 是什么--write-out让 curl 在一次传输完成之后把信息输出到标准输出。它接受一个格式字符串其中可以混排普通文本和任意数量的变量占位符格式既可以作为字面量字符串直接写在命令行上也可以让 curl 从文件读取以filename指定文件或者用-告诉 curl 从标准输入stdin读取格式。输出默认写到标准输出stdout但可以用%{stderr}与%output{}指令切换目标。值得强调的是该输出与传输是否成功无关——即使文件传输失败write-out 的输出也会照常执行而且如果 write-out 指定的动作或输出本身失败不会让 curl 返回一个不同的错误码。这意味着你可以在脚本中放心依赖 curl 的退出码做成功/失败判断同时从 write-out 输出里读取exitcode、errormsg等诊断信息src/tool_writeout.c 中ourWriteOut()以per_result携带本次传输的 CURLcode 作为参数正是为失败场景下的输出而设计。格式与转义规则所有变量都以%{variable_name}形式书写。要输出一个普通的%写成%%。支持三种转义序列序列输出内容\n换行\r回车\t制表符在 src/tool_writeout.c 的解析主循环中可以看到遇到%%直接输出一个%遇到\r、\n、\t分别映射为对应字符未知转义则原样输出两个字符。Windows 批量文件注意事项NOTE在 Windows 上%是环境变量展开的特殊符号。在批处理文件中使用本选项时所有%必须写成双写%%才能正确转义如果在命令提示符下直接使用则%无法被转义可能发生意外的环境变量展开。例如批处理中应写curl -w %%{http_code}\n https://example.com二、基础用法示例最简单的用法是输出 HTTP 响应码curl -w %{response_code}\n https://example.com该命令先正常输出页面内容最后追加一行状态码如200。注意官方示例docs/cmdline-opts/write-out.md 头部 Example 字段即为此形式。典型的只有统计、不要正文的用法配合-o /dev/null或-scurl -s -o /dev/null -w HTTP %{http_code} | 下载 %{size_download} 字节 | 总耗时 %{time_total}s\n \ https://example.com输出类似HTTP 200 | 下载 1256 字节 | 总耗时 0.143782s时间类变量time_total等以秒为单位输出且源码中统一保留 6 位小数src/tool_writeout.c 的writeTime()输出%.6格式方便直接参与数值比较。从文件或标准输入读取格式当格式串较长时推荐写入文件# format.txt 内容 # {code:%{http_code},time:%{time_total}} curl -w format.txt -o /dev/null https://example.com用-从管道读取echo %{http_code} %{size_download} | curl -w - -o /dev/null https://example.com参数解析逻辑位于 src/tool_getparam.c 的parse_writeout()当参数以开头时跳过若后续是-则从 stdin 读取否则尝试以只读文本方式打开该文件文件打开失败会返回参数读取错误。因此之后跟一个不存在的文件会直接报错而普通字符串格式则不会。三、核心变量全表以下变量均在 src/tool_writeout.c 的variables[]表中注册其中多数直接通过curl_easy_getinfo()从 libcurl 的CURLINFO_*常量取值表中ci字段即对应的 CURLINFO 枚举这也解释了变量与 libcurl API 的一一对应关系。3.1 HTTP 与协议相关变量含义引入版本http_code最后一次 HTTP(S) 或 FTP(S) 传输中发现的数字响应码早期版本response_code最后一次传输的数字响应码http_code的曾用名/新名7.18.2http_connect最后一次对代理发起的 CONNECT 请求的响应码7.12.4http_version实际使用的 HTTP 版本输出为0/1/1.1/2/3字符串7.50.0method最近一次 HTTP 请求使用的方法7.72.0content_type请求文档的 Content-Type若有早期版本scheme实际使用的 URL scheme协议7.52.0refererReferer: 请求头若有7.76.0ftp_entry_path登录 FTP 服务器后 curl 最终所在的初始路径7.15.4proxy_used前一次传输是否使用了代理1 是0 否可用于判断NOPROXY是否匹配了主机名8.7.0proxy_ssl_verify_resultHTTPS 代理的 SSL 对端证书校验结果0 表示校验成功7.52.0ssl_verify_resultSSL 对端证书校验结果0 表示校验成功7.19.0源码细节http_code与response_code在variables[]中共享同一个VAR_HTTP_CODE标识都取自CURLINFO_RESPONSE_CODEwriteLong()对VAR_HTTP_CODE和VAR_HTTP_CODE_PROXY会以%03ld三位补零输出如404、200。http_version虽底层是 long 类型但通过writeString()映射为1.1这样的字符串——代码注释明确强调Yes:http_version: 1.1No:http_version: 1.1。3.2 传输字节数与速度变量含义备注size_download下载的总字节数仅统计 body/数据部分不含头对应CURLINFO_SIZE_DOWNLOAD_Tsize_upload上传的总字节数仅统计 body/数据部分不含头对应CURLINFO_SIZE_UPLOAD_Tsize_header以 HTTP/1 风格表示的下载头部总字节数对应CURLINFO_HEADER_SIZEsize_requestHTTP 请求中发送的总字节数对应CURLINFO_REQUEST_SIZEsize_delivered保存或写入 stdout 的数据总量使用--compressed时通常与size_download不同使用--include时把响应头也计入对应CURLINFO_SIZE_DELIVEREDspeed_download整个下载过程 curl 测得的平均下载速度字节/秒对应CURLINFO_SPEED_DOWNLOAD_Tspeed_upload平均上传速度字节/秒对应CURLINFO_SPEED_UPLOAD_T3.3 时间类变量所有时间变量均以秒为单位输出内部取curl_off_t微秒值除以 1000000 得到秒余数补 6 位小数。它们分别对应 libcurl 的CURLINFO_*_TIME_T常量变量含义引入版本time_namelookup从开始到域名解析完成所花时间早期版本time_connect从开始到与远端主机或代理完成 TCP 连接所花时间早期版本time_appconnect从开始到 SSL/SSH 等 connect/handshake 完成所花时间7.19.0time_pretransfer从开始到文件传输即将开始前所花时间包含各协议特有的预传输命令与协商早期版本time_posttransfer从开始到 libcurl 发出最后一个字节所花时间8.10.0time_starttransfer从开始到收到第一个字节所花时间包含time_pretransfer以及服务器计算响应结果的时间早期版本time_redirect所有重定向步骤含域名解析、连接、预传输、传输到最终事务开始前所花总时间多次重定向时给出完整执行时间7.12.3time_queue传输在其运行期间排队的时间每次重定向步骤的排队时间都会累加当存在连接数或并行数限制时传输可能被长时间排队8.12.0time_total整个操作的总耗时早期版本一个经典的耗时分解示例curl -s -o /dev/null -w DNS:%{time_namelookup}s TCP:%{time_connect}s TLS:%{time_appconnect}s 首字节:%{time_starttransfer}s 总耗时:%{time_total}s\n \ https://example.com3.4 连接与传输标识变量含义引入版本conn_id该传输最后一次使用的连接标识在使用同一连接缓存的全部连接中唯一8.2.0xfer_id最后一次传输的数字标识若句柄上尚未开始任何传输则为 -1在使用同一连接缓存的全部传输中唯一8.2.0urlnum本次传输的 URL 索引号从 0 开始未被 glob 展开的 URL 与原始 glob URL 共享同一索引7.75.0num_connects最近一次传输中新建立的连接数7.12.3num_redirects请求中跟随的重定向次数7.12.3num_headers最近一次请求的响应头数量每次重定向时重新计数注意状态行不算头部7.73.0num_retries使用--retry时实际执行的重试次数8.9.0num_certsTLS 握手中收到的服务器证书数量仅 OpenSSL、GnuTLS、Schannel 和 Rustls 后端支持7.88.03.5 网络地址信息变量含义引入版本local_ip最近一次连接本地端的 IP 地址IPv4 或 IPv67.29.0local_port最近一次连接的本地端口号7.29.0remote_ip最近一次连接远端对端的 IP 地址IPv4 或 IPv67.29.0remote_port最近一次连接的远端端口号7.29.03.6 URL 解析变量url.*系列针对输入 URLurle.*系列针对有效最终URL即跟随重定向后的最后一个 URL等价于url_effective。这些变量在源码中通过curl_url()libcurl URL API解析输入 URL 直接解析urle.*则先取CURLINFO_EFFECTIVE_URL再解析见 src/tool_writeout.c 的urlpart()函数内部使用CURLUPART_*枚举逐段拆分。变量含义引入版本url被请求的 URL7.75.0url_effective最后实际请求的 URL配合--location跟随重定向时最有意义早期版本url.scheme/urle.schemeURL 的 scheme 部分8.1.0url.user/urle.userURL 的用户名部分8.1.0url.password/urle.passwordURL 的密码部分8.1.0url.options/urle.optionsURL 的选项部分8.1.0url.host/urle.hostURL 的主机部分8.1.0url.port/urle.portURL 的端口号未显式指定且 scheme 已知时显示该 scheme 的默认端口8.1.0url.path/urle.pathURL 的路径部分8.1.0url.query/urle.queryURL 的查询部分8.1.0url.fragment/urle.fragmentURL 的 fragment锚点部分8.1.0url.zoneid/urle.zoneidURL 的 zone id 部分8.1.03.7 文件、证书与错误变量含义引入版本filename_effectivecurl 实际写入的目标文件名仅当配合--remote-name或--output写文件时有意义与--remote-header-name组合最有用7.26.0certs输出证书链及详细信息仅 OpenSSL、GnuTLS、Schannel 和 Rustls 后端支持7.88.0errormsg错误消息7.75.0exitcode传输的数字退出码7.75.0tls_earlydata作为 TLSv1.3 early data 发送的字节数未使用该特性时为 0被服务器拒绝时为负值通过--tls-earlydata启用8.13.0redirect_url未使用--location跟随重定向或达到--max-redirs上限时显示重定向本会前往的实际 URL7.18.2源码佐证certs与num_certs都通过CURLINFO_CERTINFO获取证书信息certinfo()辅助函数缓存结果errormsg优先取句柄错误缓冲区per-errorbuffer否则用curl_easy_strerror()把 CURLcode 转成可读字符串exitcode直接等于本次传输的per_resultCURLcode 数值。四、进阶指令%header{}、%output{}、%stderr 与 %stdout4.1 %header{name}输出响应头值用%header{name}输出传输最近一次服务器响应中指定头的值name是不区分大小写的头名不含末尾冒号。头内容与线上传输完全一致但会去掉首尾空白与换行。该特性引入于 7.84.0。curl -s -w Date: %header{date}\nServer: %header{server}\n -o /dev/null https://example.com注意与其他变量不同header这个名字不在花括号里花括号里放的是头名例如%header{date}。源码中output_header()通过curl_easy_header()在最近一次响应中按名查找头部并原样输出值src/tool_writeout.c。多响应/重定向链场景8.17.0 起在头名后追加:all:[separator]可输出所有同名头字段——包括整个重定向链上每一跳出现的该头。[separator]若不为空在有多个头时作为它们之间的分隔字符串输出多个头按线上出现的时间先后顺序输出。若分隔符中要包含右花括号}需用反斜杠转义为\}。# 输出每次重定向响应中的 Location 头用 - 连接 curl -sL -w %header{location:all: - }\n -o /dev/null http://example.com源码中separator()专门处理分隔符的转义\r、\n、\t、\}均被解码未知转义原样输出。4.2 %output{filename}重定向输出到文件用%output{name}把此后的 write-out 输出写入指定文件name为完整文件名引入于 8.3.0。同一 write-out 参数中可以出现多个%output{}指令。若文件无法创建curl 保持使用该指令之前的输出目标不变。用%output{name}则向已存在文件追加数据。# 把统计写入 stats.txt页面正文仍输出到终端 curl -o /dev/null -w %{http_code} %{time_total}\n%output{stats.txt} https://example.com同样地output名字本身不在花括号内花括号里是文件名例如%output{stats.txt}。源码实现中%output{解析后先检查是否以开头决定追加模式用curlx_fopen()打开目标文件只有打开成功才切换输出流——失败则沿用之前的流与文档描述一致。4.3 %{stderr} 与 %{stdout}切换输出流%{stderr}从该点起write-out 输出写到标准错误7.63.0 起%{stdout}从该点起恢复写标准输出默认即此但可在切到 stderr 后切回。# 统计信息走 stderr正常内容走 stdout curl -s -o /dev/null -w ok -w %{stderr}ERR: %{errormsg} https://example.com4.4 %{onerror}仅出错时输出%{onerror}让其后的输出仅在传输返回非零错误时显示7.75.0 起# 只有失败时才打印诊断信息 curl -s -w %{onerror}失败: %{errormsg} (退出码 %{exitcode})\n https://example.com源码中该变量通过per_result CURLE_OK判断成功则置done TRUE跳过剩余格式串。五、%{json} 与 %{header_json}机器可读输出%{json}7.70.0 起输出一个 JSON 对象包含除header_json之外的全部可用键%{header_json}7.83.0 起输出一个 JSON 对象包含最近一次传输的全部 HTTP 响应头。值以数组形式给出因为同一头名可能多次出现多个值。header_json的头名统一为小写按线上出现顺序排列重复的头会被归并到该头第一次出现的位置每个值依次放入 JSON 数组见 src/tool_writeout_json.c 的headerJSON()它用curl_easy_nextheader()遍历响应头amount 1时展开成 JSON 列表。字符串转义\、、控制字符等由jsonquoted()/jsonWriteString()完成。# 完整 JSON 统计 curl -s -o /dev/null -w %{json}\n https://example.com # 只输出响应头 JSON curl -s -o /dev/null -w %{header_json}\n https://example.comheader_json输出示例键为小写头名值为数组{date:[Tue, 09 Sep 2026 00:00:00 GMT],content-type:[text/html; charsetUTF-8],server:[gws]}另外ourWriteOutJSON()输出的 JSON 末尾还固定附带一个curl_version键记录 curl 版本字符串便于日志自描述。六、%time{format}格式化当前 UTC 时间%time{format}8.16.0 起输出当前UTC时间花括号内使用strftime()格式。要显示时间时{}内的字符构成特殊格式串可包含称为转换说明符的特殊字符序列每个转换说明符以%开头后跟一个指示 curl 输出特定时间细节的字符其余字符按原样显示。curl -s -o /dev/null -w [%time{%Y-%m-%d %H:%M:%S}] HTTP %{http_code}\n https://example.com # 输出示例[2026-09-09 02:09:26] HTTP 200由于时间恒为 UTC源码outtime()函数做了两个可移植性特判%z固定输出0000%Z固定输出UTC另外%f是 curl 特有的微秒说明符%s由 curl 自行计算避免mktime()的本地时区问题。完整转换说明符表说明符含义%a当前语言环境下的星期缩写名%A当前语言环境下的星期全名%b当前语言环境下的月份缩写名%B当前语言环境下的月份全名%c当前语言环境的首选日期和时间表示POSIX 语言环境下等价于%a %b %e %H:%M:%S %Y%C世纪数year/100的 2 位整数%d月中日期十进制数01 至 31%D等价于%m/%d/%y国际语境下有歧义应避免使用%e同%d月中日期但前导零替换为空格%f当前秒内经过的微秒数curl 特有代码非标准%F等价于%Y-%m-%dISO 8601 日期格式%G带世纪的 ISO 8601 基于周的年若 ISO 周号属于上一年或下一年则使用对应年份%g同%G但不含世纪2 位年份00-99%h等价于%b%H24 小时制小时00 至 23%I12 小时制小时01 至 12%j年内日序号001 至 366%k24 小时制小时0 至 23个位数前加空格%l12 小时制小时1 至 12个位数前加空格%m月份01 至 12%M分钟00 至 59%pAM 或 PM或当前语言环境的对应字符串正午视为 PM午夜视为 AM%P同%p但为小写am/pm 或当前语言环境的对应字符串%r上午/下午am/pm记法的时间%R24 小时记法%H:%M含秒的版本见%T%s自 Epoch1970-01-01 00:00:00 0000 UTC以来的秒数%S秒00 至 60上限 60 是为偶尔的闰秒留余地微秒见%f%T24 小时记法%H:%M:%S%u星期几的十进制数1 至 7周一为 1%U年内周号00 至 53以第一个星期日作为第 01 周的第一天另见%V、%W%VISO 8601 年内周号01 至 53第 1 周是新年中至少有 4 天的那一周另见%U、%W%w星期几的十进制数0 至 6周日为 0另见%u%W年内周号00 至 53以第一个星期一作为第 01 周的第一天%x当前语言环境的首选日期表示不含时间%X当前语言环境的首选时间表示不含日期%y不带世纪的年份00 至 99%Y带世纪的年份%zhhmm或-hhmm数字时区即相对 UTC 的小时分钟偏移因时间恒为 UTC恒输出0000%Z时区名恒为GMT%%字面%字符七、实战监控脚本与测试场景7.1 简单的健康检查脚本#!/bin/bash code$(curl -s -o /dev/null -w %{http_code} --max-time 10 https://example.com/api/health) if [ $code 200 ]; then echo OK else echo FAIL: $code fi7.2 上传 下载一体化度量curl -s -w 上传 %{size_upload} 字节, 下载 %{size_download} 字节\n \ -F filereport.pdf https://example.com/upload7.3 重定向链分析# 查看所有重定向目标 curl -sL -o /dev/null -w 最终URL: %{url_effective}\n重定向次数: %{num_redirects}\n各跳Location: %header{location:all: - }\n \ http://example.com7.4 与库 API 的对应关系write-out 的大部分变量与 libcurl 的curl_easy_getinfo()常量一一对应src/tool_writeout.c 的variables[]表ci字段这意味着同样的数据在 C 程序里可通过CURLINFO_RESPONSE_CODE、CURLINFO_TOTAL_TIME_T、CURLINFO_SIZE_DOWNLOAD_T等直接获取。命令行用户与库使用者看到的是同一套数据源。八、测试覆盖与验证仓库 tests/data 下存在大量使用--write-out的回归测试如 test1029、test1067、test1080、test1081、test1089、test1090、test1159、test1164、test1188、test1197 等它们用command段中的-w %{...}格式与stdout段中的期望输出逐一比对覆盖了http_code、time_*、size_*、url_effective等关键变量的输出正确性。修改或新增 write-out 行为时这些测试是保证格式输出不回归的重要防线。九、使用建议小结先 -o /dev/null 再 -w大多数监控场景不需要正文用-o /dev/null丢弃响应体只保留 write-out 统计。善用%%、\n、\t格式串中%和转义序列必须按规则书写Windows 批处理中%一律双写。用%{onerror}做条件诊断把错误信息输出与正常统计分开脚本更清晰。用%{json}对接程序需要解析时优先使用 JSON 输出避免字符串截取。用%time{%F %T}打时间戳UTC 时间保证跨机器、跨时区日志一致%f可追加微秒精度。注意版本门槛表格中已标注每个变量的引入版本如time_queue需 8.12.0、%time{}需 8.16.0、header:all:需 8.17.0在旧版本 curl 上使用会提示 unknown --write-out variable。相关文档选项定义与更新记录docs/cmdline-opts/write-out.md输出实现源码src/tool_writeout.c、src/tool_writeout_json.c参数解析逻辑src/tool_getparam.c变量标识符枚举src/tool_writeout.h同类输出相关选项verbose、head【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考