使用AWK打印XML元素

3

我该如何使用AWK打印XML元素的内容(包含起始标签和结束标签)?

比如,考虑以下XML示例:

<flight>
    <airline>Delta</airline>
    <flightno>22</flightno>
    <origin>Atlanta</origin>
    <destination>Paris</destination>
    <departure>5:40pm</departure>
    <arrival>8:10am</arrival>
</flight>
<city id="AT"> 
       <cityname>Athens</cityname> 
       <state>GA</state>
       <description> Home of the University of Georgia</description>
       <population>100,000</population>
       <location>Located about 60 miles Northeast of Atlanta</location>
       <latitude>33 57' 39" N</latitude>
       <longitude>83 22' 42" W</longitude>
</city>

期望的输出可以是 city 元素的内容,从 <city...></city>
2个回答

5

使用类似awk和sed的工具解析XML的解决方案并不完美。您不能指望XML总是具有人类可读的布局。例如,一些Web服务将省略换行符,导致整个XML文档显示在一行上。

我建议使用xmllint,它具有使用XPATH选择节点的能力,这是专为XML设计的查询语言。

以下命令将选择城市标签:

xmllint --xpath "//city" data.xml

XPath非常有用。它使XML文档的每个部分都可以被寻址:
xmllint --xpath "string(//city[1]/@id)" data.xml

返回字符串 "AT"。

格式不良的 XML 数据

这次返回第一个出现的 "city" 标签。可以使用 xmllint 让结果更易读:

$ xmllint --xpath "//city[1]" data.xml  | xmllint -format -
<?xml version="1.0"?>
<city id="AT">
  <cityname>Athens</cityname>
  <state>GA</state>
  <description> Home of the University of Georgia</description>
  <population>100,000</population>
  <location>Located about 60 miles Northeast of Atlanta</location>
  <latitude>33 57' 39" N</latitude>
  <longitude>83 22' 42" W</longitude>
</city>

data.xml

在这个数据中,第一个“city”标签出现在同一行上。这是有效的XML。

<data>
  <flight>
    <airline>Delta</airline>
    <flightno>22</flightno>
    <origin>Atlanta</origin>
    <destination>Paris</destination>
    <departure>5:40pm</departure>
    <arrival>8:10am</arrival>
  </flight>
  <city id="AT"> <cityname>Athens</cityname> <state>GA</state> <description> Home of the University of Georgia</description> <population>100,000</population> <location>Located about 60 miles Northeast of Atlanta</location> <latitude>33 57' 39" N</latitude> <longitude>83 22' 42" W</longitude> </city>
  <city id="DUB">
    <cityname>Dublin</cityname>
    <state>Dub</state>
    <description> Dublin</description>
    <population>1,500,000</population>
    <location>Ireland</location>
    <latitude>NA</latitude>
    <longitude>NA</longitude>
  </city>
</data>

唯一的问题是在提取数据之前必须修复xml。例如,我有一堆没有引号的属性值文件。由于xmllint的工作是查找问题,因此它会变得疯狂。 - Jeter-work

2
$ awk -v tag='city' '$0~"^<"tag"\\>"{inTag=1} inTag; $0~"^</"tag">"{inTag=0}' file
<city id="AT">
       <cityname>Athens</cityname>
       <state>GA</state>
       <description> Home of the University of Georgia</description>
       <population>100,000</population>
       <location>Located about 60 miles Northeast of Atlanta</location>
       <latitude>33 57' 39" N</latitude>
       <longitude>83 22' 42" W</longitude>
</city>

使用GNU awk进行\>单词边界功能。对于其他的awk,使用[^[:alnum:]_]或类似的方法。

只打印第一次出现的内容:

$ awk -v tag='city' '$0~"^<"tag"\\>"{inTag=1} inTag{print; if ($0~"^</"tag">") exit}' file
<city id="AT">
       <cityname>Athens</cityname>
       <state>GA</state>
       <description> Home of the University of Georgia</description>
       <population>100,000</population>
       <location>Located about 60 miles Northeast of Atlanta</location>
       <latitude>33 57' 39" N</latitude>
       <longitude>83 22' 42" W</longitude>
</city>

如果有两个城市,它会打印出两个。我只想要第一个。 - Adam Matan
2
有一个微不足道的调整可以解决这个问题,但是如果你发布的问题和代表性的样例输入输出并不能真正反映出你想要的内容,那么请适当更新你的问题,这样我们就不会浪费时间去猜测你下一个需求变更可能是什么了。 - Ed Morton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接