服务器与大带宽专家 · 持牌IDC/CDN/ISP服务商
简米科技官网JIANMI TECH
资讯 2026-11-07 简米科技 5,231 字 13 分钟阅读

如何用jq从URL中精准提取域名信息?,jq如何提取域名

导读要用jq从URL中精准提取域名,最可靠的方式不是让jq直接解析URL,而是先用正则捕获或前置转换把URL整理成JSON字段,再用jq的过滤表达式取host部分,很多人以为jq既然是JSON处理器,那处理URL肯定也顺手,其实不然,jq只认JSON结构,你丢给它一个裸的URL字符串,它只会报错或者原样返回,真正高……

要用jq从URL中精准提取域名,最可靠的方式不是让jq直接解析URL,而是先用正则捕获或前置转换把URL整理成JSON字段,再用jq的过滤表达式取host部分。

很多人以为jq既然是JSON处理器,那处理URL肯定也顺手,其实不然,jq只认JSON结构,你丢给它一个裸的URL字符串,它只会报错或者原样返回,真正高效的做法,是把“提取域名”拆成两个动作:先用正则或字符串操作把host从URL里抠出来,再用jq做批量过滤和格式化,下面这套方法,能覆盖从单条URL到百万行日志的大多数场景。

jq解析url提取域名,为什么直接操作往往失败

先看一个典型的翻车现场:

echo "https://example.com/path" | jq '.'

jq会提示parse error: Invalid literal,原因很简单,jq的输入必须是合法的JSON,URL既不带引号,也不是对象,自然不满足要求,有些人会先把URL加引号再试:

echo '"https://example.com/path"' | jq -r '.'

这样jq能输出原字符串了,但你拿到的还是整个URL,没有拆出域名,业内专家指出,jq的职责边界在“结构处理”而不是“语法解析”,你让jq做它不擅长的事,结果就是绕弯路。

正确的思路是:把URL当作一个普通字符串,先用正则把域名部分捕获出来,再用jq对捕获结果做二次加工,这也是下面所有方法的基本前提。

把URL变成JSON:三种最实用的前置处理法

用python或node生成JSON,再交给jq

当你要处理的是几十上百条URL时,建议一次性把它们转成JSON数组,再交给jq,这样jq能发挥原生优势,批量操作。

python3 -c "import sys,json; urls=[line.strip() for line in sys.stdin if line.strip()]; print(json.dumps(urls))" < urls.txt | jq -r '.[]'

你还可以在python里直接解析域名,但那就绕过了jq,如果你就是想在管道里用jq,那就保持这种分工,node也可以做同样的事,但python写起来更短,大多数日志分析脚本里,这个前置步骤几乎成了惯例。

用jq正则capture直接匹配

如果只是几条URL,不想引入额外的解释器,可以用jq自带的capture函数,jq从1.5版本开始支持正则,配合-R参数按行读取原始文本,就能直接提取host。

echo 'https://blog.example.com.cn:8080/path?q=1' | jq -R -r 'capture("^(?<scheme>[a-zA-Z][a-zA-Z0-9+.-]://)?(?<host>[^/:?#]+)") | .host'

输出:

blog.example.com.cn

关键在于(?<host>...)命名捕获组,捕获后jq会生成一个对象,你可以直接取.host字段,这个正则已经考虑了协议可选、host不包含冒号和斜杠的情况,比简单的split("/")[2]健壮得多。

用sed/grep预处理后再交给jq

有时你面对的数据量极大,比如几个GB的nginx日志,这时候纯jq正则处理可能会变慢,因为jq的每次匹配都带着JSON解析开销,先用sed或grep做粗筛,效率会高一个量级。

sed -E 's#^[a-zA-Z][a-zA-Z0-9+.-]://##' urls.txt | cut -d/ -f1 | jq -R -r 'select(length > 0)'

sed负责剥掉协议,cut负责截取第一个斜杠前的部分,这时再交给jq,jq只需要做校验和格式化,压力小很多,这个组合在processing日志时很实用,尤其是当你还要统计域名出现次数的时候。

从JSON日志中精准提取域名的完整流程

实际场景里,我们处理得最多的并不是裸URL,而是JSON格式的访问日志,比如一行一条记录,里面有request_url字段:

{"ip":"1.2.3.4","time":"2026-01-01","request_url":"https://docs.example.com/api?from=alert"}

这种结构下,jq的威力才能彻底发挥出来。

第一步:过滤空URL和无效行

日志里总有空字段或脏数据,直接提取会报错,所以先用select把有效行筛出来:

jq -r 'select(.request_url != null and .request_url != "") | .request_url' access.json

第二步:用命名捕获组提取host

接着把提取逻辑接上去:

jq -r 'select(.request_url != null and .request_url != "") | .request_url | capture("^(?<scheme>[a-zA-Z][a-zA-Z0-9+.-]://)?(?<host>[^/:?#]+)") | .host' access.json

这条命令能直接输出纯字符串的域名列表,你也可以把scheme一起捕获,方便后续按协议分组。

第三步:按需拆分端口、路径和query

有些场景还需要端口、路径,把正则扩展一下:

jq -r '.request_url | capture("^(?<scheme>[a-zA-Z][a-zA-Z0-9+.-]://)?(?<host>[^/:?#]+)(:(?<port>[0-9]+))?(?<path>/[^?#])?") | [.scheme, .host, .port, .path] | @tsv'

输出结果用Tab分隔,方便导入表格,注意port和path是可选的,没匹配到就是null,可以后续用给默认值。

第四步:把提取结果接入统计管道

拿到域名列表后,下一步往往是统计次数,用jq自身就能完成排序和计数:

jq -r '.request_url | capture("...").host' access.json | sort | uniq -c | sort -rn

如果你不想离开jq,也可以用group_by:

jq -r '.request_url | capture("...").host' access.json | jq -s 'group_by(.) | map({host: .[0], count: length}) | sort_by(-.count)'

两种方式都行,前者更省内存,后者更符合jq风格,数据量不大时,用jq -s全部读入内存很简洁。

url提取域名命令,性能优化的几个关键点

很多人在处理几十万行日志时发现命令跑得很慢,其实多数情况下不是jq本身慢,而是用法不对,这里有几个可以明显提速的细节。

  • 避免在循环里反复执行jq,每次执行jq都要启动一次进程,开销巨大,正确做法是把所有URL写进一个文件,用jq -R按行处理,一次搞定。
  • 尽量先用grep过滤掉不相关行,比如你只关心https请求,先grep 'https://',再交给jq,能少解析很多数据。
  • 不要滥用`.匹配,正则里的.容易引起回溯变慢,尤其是URL特别长的时候,用[^/:?#]+`这种字符类替代,既精准又高效。
  • 大文件优先用sort | uniq -c统计,而不是group_by,因为group_by需要把整个数组加载进内存,几GB的文件直接内存溢出。

为了让你快速选择,这里列一张常用表达式对照表:

需求 推荐命令 说明
从JSON字段提取host jq -r '.url \| capture("...").host' 输入必须是合法JSON
从纯文本列表提取host jq -R -r 'capture("...").host' -R按行读取原始字符
提取host并过滤空值 jq -r 'select(.url != "") \| .url \| capture("...").host' 先过滤再提取
提取根域名(简化规则) jq -R -r 'capture("(?<host>[^/:?#]+)").host \| split(".") \| .[-2:] \| join(".")' 不适用于多级公共后缀
统计域名出现次数 jq -r 'capture("...").host' file \| sort \| uniq -c \| sort -rn 内存占用最低

注意表格中的\|是Markdown转义,实际命令里用。

jq从URL提取域名的边角问题

host里带用户名密码的情况

有些URL长这样:https://user:pass@example.com/path,之前的正则会把user:pass@example.com整体当成host,解决办法是在正则里增加一个可选的userinfo捕获组:

capture("^(?<scheme>[a-zA-Z][a-zA-Z0-9+.-]://)?(?:(?<userinfo>[^@/?#])@)?(?<host>[^/:?#]+)")

提取时仍然取.host,就能拿到干净的example.com。

URL里没有协议的情况

比如example.com/path或example.com,正则的scheme部分已经是可选,所以能正常匹配,但如果URL以开头,比如//example.com/path,则scheme部分匹配不到,host也能正确捕获。

如何提取真正的根域名

blog.example.com.cn这类多级域名,简单取最后两段会得到com.cn,要处理好,需要一份公共后缀列表,或者对已知后缀做特殊判断,如果只是内部统计,把规则写成“最后两段或三段”即可,不要期望jq里有一行内置命令能解决这个问题,它毕竟不是专业的URL解析库。

关于jq从URL中提取域名的常见问题

jq解析url提取域名,有没有更简单的内置函数?

没有,jq官方文档里没有提供url或domain之类的解析函数,最接近的做法是先用split("?")[0]去掉查询参数,再用split("/")[2]取域名,这种方式对简单URL有效,但遇到端口和协议缺失时会出错,相比之下,capture加正则是最通用的方案,建议直接掌握它。

url提取域名命令,怎么处理带端口和路径的复杂URL?

只要正则里把host部分定义为[^/:?#]+,它天然会停在端口、路径和查询参数之前,如果你还需要保留端口,可以在host后面加(:(?<port>[0-9]+))?捕获组,这样example.com:8080就能拆出host为example.com,port为8080。

一堆URL里混合了http和https,协议会影响提取结果吗?

不会,正则里scheme部分设计成可选的,而且只匹配合法协议字符,所以http://、https://、ftp://都会被跳过,最终只留下host部分,即使URL没有协议,也能正常提取,这也是这个正则比硬编码https更可靠的原因。

回到根本问题上:jq不是URL解析器,但你可以把它变成一个高效的域名提取工具,关键就是两条用命名捕获组把host抠出来,用-R或JSON流让jq处理原始行,掌握了这两个核心点,无论是单条URL还是海量日志,都不会再卡壳。

分享本文
本文为 简米科技官网 原创,已由运维技术专家审核。转载请注明来源:原文链接
售前咨询 服务热线 售后 邮箱