我想使用命令读取两个 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>
我可以使用grep
和sed
命令的组合来完成此操作,如下所示,
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'
挑战:
- 文件的元素中可以有或没有名称空间前缀。
- 该文件相当大,大约100Mb或更多。
- 文件内容可以是格式正确的 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