使用 sed 命令读取 XML 中元素之间的单词

使用 sed 命令读取 XML 中元素之间的单词

我想使用命令读取两个 xml 元素之间的单词sed

例如,在下面的 xml 中,我想读取数字 1234567。

 <ns1:account>
    <ns2:name>Corporation</ns2:name>
    <address>
        <StrtNm>NewYork</StrtNm>
        <BldgNb>3</BldgNb>
        <PstCd>230300</PstCd>
        <Ctry>USA</Ctry>
    </address>
</ns1:account>
<ns3:details>
<ns4:accnum>
    <ns5:info>
        <nd6:accnum>1234567</nd6:accnum>
    </ns5:info>
</ns4:accnum>
</ns3:details>

我可以使用grepsed命令的组合来完成此操作,如下所示,

grep -oz '<.*details>\s*<.*accnum>\s*<.*info>\s*<.*accnum>[0-9]*</.*accnum>' test.xml |sed -n 's:.*<.*accnum>\(.*\)</.*accnum>.*:\1:p'

但我读到这grep -oz对性能不利,因为它将整个文件视为一行。因此,我尝试使用两个sed命令,但只有当文件格式正确(如上所示)时它才有效。如果 xml 是单行且没有漂亮的打印,则它不起作用。这是我尝试过的:

sed -n '/.*details>/,/<\/.*accnum>/p' test.xml |sed -n 's:.*<.*accnum>\(.*\)<.*accnum>:\1:p'

挑战:

  1. 文件的元素中可以有或没有名称空间前缀。
  2. 该文件相当大,大约100Mb或更多。
  3. 文件内容可以是格式正确的 xml,也可以是作为单行的整个 xml。

我还没有尝试过awk命令,因为我们的应用程序中存在使用上面列出的命令的现有脚本,我希望得到相同的工作。

答案1

我必须编辑您的 XML 以使其成为格式良好的文档(添加元素<root/>并声明名称空间):

<?xml version="1.0"?>
<root xmlns:ns1="urn:ns1" xmlns:ns2="urn:ns2" xmlns:ns3="urn:ns3" xmlns:ns4="urn:ns4" xmlns:ns5="urn:ns5" xmlns:nd6="urn:nd6">
  <ns1:account>
    <ns2:name>Corporation</ns2:name>
    <address>
      <StrtNm>NewYork</StrtNm>
      <BldgNb>3</BldgNb>
      <PstCd>230300</PstCd>
      <Ctry>USA</Ctry>
    </address>
  </ns1:account>
  <ns3:details>
    <ns4:accnum>
      <ns5:info>
        <nd6:accnum>1234567</nd6:accnum>
      </ns5:info>
    </ns4:accnum>
  </ns3:details>
</root>

完成此操作后,我可以用来xmlstarlet解析 XML 文件并精确提取您需要的元素

xmlstarlet sel -t -v '//nd6:accnum' -n x.xml
1234567

您可以根据需要修改 XPath 以使其更加精确。例如,/root/ns3:details/ns4:accnum/ns5:info/nd6:accnum这将是一个极端的选择。

如果您没有xmlstarlet可用,我强烈建议您安装它。如果系统不属于您管理,请将其作为您正在进行的项目的先决条件。尝试使用sed和解析 XML 文件awk在短期内会起作用,但它会在以后产生技术债务,特别是如果您无法控制 XML 文档的精确布局(空格、换行符、注释等)。

答案2

使用 xidel 和有效的 xml 输入(参见@roaima 答案),我们可以:

xidel   -se  '//nd6:accnum/text()'  file.xml

在哪里

  • //nd6:accnum/text()是一个 XPath 表达式,用于在任何位置查找元素“nd6:accnum”并选择其文本。

答案3

这个单行 perl 命令将打印预期结果:

perl -lne 'print "$1" if /<nd6:accnum>(\w+)</' file.xml
1234567

相关内容