我有一个很大的 XML 文件,我得到了 2 个标签之间出现的所有事件:
这是我所做的:
sed -n '/<tag>/,/<\/tag>/p' file.xml
我需要过滤以仅获取前 N 个出现的情况。我尝试过 l param 但还不够:(
那么有人知道如何从所有结果集中获取 N 个匹配的事件吗?
例如。这里是 xml 文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
</root>
sed -n '/<tag>/,/<\/tag>/p' file.xml
返回所有元素。
所以目标是过滤以获得前 n 个匹配模式(元素是多行)如果 n = 2 那么结果 =:
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
答案1
尝试:
xmllint --xpath '//tag[position()<=2]' file.xml
或者:
xmlstarlet sel -t -c '//tag[position()<=2]' file.xml
或者:
xmlstarlet sel -t -m '//tag[position()<=2]' -c . -n file.xml
如果你想sed
只用你可以这样做:
sed -n '
1{x;s/^/../;x;}; # initialise counter with two tokens
/<tag>/,/<\/tag>/ {
p; /<\/tag>/{
x;s/.//;/./!q;x; # remove a token and quit if hold space empty
}
}' file.xml
也就是说,使用保留空间作为要显示的剩余部分的计数器(使用点字符)。
答案2
您确实应该为此使用解析器,但是,正如您所知,sed -n '/<tag>/,/<\/tag>/p' file.xml
它会获取所有元素,因为您p
将它们全部打印出来。该命令的工作原理是对输入中包含 的行<tag>
和包含 的下一行之间的所有行进行寻址</tag>
。因为这几乎构成了你所有的台词,所以仅仅p
打印它们并不会显示出太大的差异。像下面这样的东西可能更接近目标:
sed -n '\|<tag>|{:n
\|</tag>|!{N;bn}
y|\n| |;p
}'
它对<tag>
行进行寻址并检查它们的</tag>
.如果它们不包含结束字符串,则会拉入另一行 - 并且会重复执行此操作,直到模式空间包含<tag>.*</tag>[^\n]*$
.
然后我将\n
模式空间中的所有 ewline 字符转换为空格。
又是这样:
sed -n '\|<tag>|{:n;\|</tag>|!{N;bn};y|\n| |;p}' <<\DATA
<?xml version="1.0" encoding="UTF-8"?>
<root>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
</root>
DATA
输出:
<tag> <t1>john</t1> <t2>john</t2> <t3>john</t3> </tag>
<tag> <t1>john</t1> <t2>john</t2> <t3>john</t3> </tag>
<tag> <t1>john</t1> <t2>john</t2> <t3>john</t3> </tag>
<tag> <t1>john</t1> <t2>john</t2> <t3>john</t3> </tag>
现在你可能会这样做:
sed -n '\|<tag>|{:n
\|</tag>|!{N;bn}
y|\n| |;p
}' ./file |
sed 's|> |>\n|g;2q'
...这让我:
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
<tag>
<t1>john</t1>
<t2>john</t2>
<t3>john</t3>
</tag>
答案3
我想这就是你想要的
sed -n '/<tag>/,/<\/tag>/p' file.xml | head -10
尝试以下命令获取以<tag>
,开头的前两行
$ sed -n '/^<tag>/p' file.xml | head -2
<tag><t1>john</t1></tag>
<tag><t1>john</t1></tag>
答案4
据我所知,sed
匹配总是贪婪的,/<tag>/,/<\/tag>/
即将从第一个实例<tag>
到最后一个实例进行匹配<\tag>
- 包括之间的任何其他 XML 对象。
如果您的版本awk
支持多字符记录分隔符,您也许可以执行类似的操作
awk -v n=2 'BEGIN{RS="</tag>\n";ORS=RS} NR<=n'
但真正更强大的解决方案是使用专用的 XML 解析器 - 例如使用 python 的非常简单的实现minidom
#!/usr/bin/python
from xml.dom import minidom
xmldoc = minidom.parse('file.xml')
taglist = xmldoc.getElementsByTagName('tag')
for i in range(2) :
print taglist[i].toxml()