如何在Shell脚本中解析XML?求xmllint/grep实现指定输出
问题描述
需要从给定的XML文件中提取以下格式的输出:
availStart="2024-05-24T05:00:30" availId="811220455" q1:campaignIdRef=180398 availStart="2024-05-24T05:13:41" availId="811220484" q2:campaignIdRef=179163 availStart="2024-05-24T05:13:41" availId="811220484" q2:campaignIdRef=179166 availStart="2024-05-24T05:13:41" availId="811220484" q2:campaignIdRef=179164 availStart="2024-05-24T05:13:41" availId="811220484" q2:campaignIdRef=179165
尝试过以下命令,但仅能获取availStart值,availId无输出,且无法关联campaignIdRef:
while read LINE; do time=`grep -io -P 'availstart=.{20}'` && avail=`grep -io -P 'availId=.{11}'`;echo -e "\n$time and $avail";done < sample.xml
解决方案
方法一:使用xmllint(推荐,XML专用工具)
XML包含命名空间,需在XPath中声明才能正确定位元素。以下命令通过xmllint提取所需字段,再用awk格式化输出:
xmllint --xpath "declare default element namespace 'urn:scte:118:version01'; declare namespace q='urn:nds:dynamic:ruleManagerICD:01:01'; /Schedule/Window/Avail/(./@availStart, ./@availId, .//q:campaignIdRef/text())" sample.xml | awk '{ if (match($0, /availStart="[^"]+"/)) start=substr($0,RSTART,RLENGTH); if (match($0, /availId="[^"]+"/)) id=substr($0,RSTART,RLENGTH); if ($0 ~ /^[0-9]+$/) { prefix = (start ~ /2024-05-24T05:00:30/) ? "q1:" : "q2:"; print start " " id " " prefix "campaignIdRef=" $0; } }'
说明:
xmllint的--xpath参数声明了XML的默认命名空间和q前缀(对应q1/q2的统一URI),提取每个Avail节点的availStart、availId属性,以及所有后代的campaignIdRef文本值。awk负责缓存availStart和availId,遇到数字格式的campaignIdRef时,根据时间匹配前缀并输出完整行。
方法二:使用awk+正则(快速处理结构固定的XML)
如果XML结构稳定,可直接用awk逐行扫描,关联Avail节点和对应的campaignIdRef:
awk ' /<Avail/ { # 提取当前Avail的availStart和availId属性 match($0, /availStart="[^"]+"/, start_match); match($0, /availId="[^"]+"/, id_match); current_start = start_match[0]; current_id = id_match[0]; # 提取当前Avail的命名空间前缀(q1/q2) match($0, /xmlns:(q[12])="urn:nds/, prefix_match); current_prefix = prefix_match[1] ":"; } /campaignIdRef>/ { # 提取campaignIdRef的值并输出完整行 match($0, /campaignIdRef>([0-9]+)</, camp_match); print current_start " " current_id " " current_prefix "campaignIdRef=" camp_match[1]; } ' sample.xml
说明:
- 遇到
<Avail>行时,缓存该行的availStart、availId和命名空间前缀。 - 遇到
campaignIdRef行时,直接使用缓存的Avail信息,输出目标格式的行。
原命令问题分析
你之前的while read循环存在两个核心问题:
- 循环内的
grep默认读取整个文件,而非当前行,导致变量值重复且无法关联上下文。 - 没有处理
campaignIdRef与对应Avail节点的关联关系,无法生成目标格式的输出。
内容的提问来源于stack exchange,提问作者Hasham1983
相关产品推荐
相关产品推荐

