在bash中,如何解析字符串以删除除两个标记之间的内容之外的所有内容?

时间:2017-03-15 02:02:35

标签: bash sed

将此标记为重复的用户错过了树木的森林,他们建议的副本不能充分回答这个问题。

以下是此字符串的示例:

<mobile_device><general><id>15</id><device_name>iPad</device_name><name>Timmy</name><asset_tag/><id>16</id><device_name>iPhone</device_name><name>Spike</name><asset_tag/></general></mobile_device>

我想以某种方式解析这个问题,结果只有:

<id>15</id><id>16</id>

因此,删除开始标记标记和结束标记标记之间未包含的所有内容,并且可能存在无限量的标记。 (虽然更现实的上限边缘情况是60,000)但是总会有至少一对标签。

我一直在玩sed,但这种语法的变化根本没用:

sed 's/.*\(<id>*</id>\).*//'

非常感谢任何指导!

6 个答案:

答案 0 :(得分:1)

假设您的数据位于input.xml,这里使用xmllint和简单的XPath查询

$ cat input.xml | xmllint --xpath '//id' -
<id>15</id><id>16</id>

如果<id>...</id>或更合适的工具不可用,您可以使用这些快速而肮脏的内容来提取xmllint之间的信息。

$ cat input.xml | perl -pe 's/(<.?id.)/\n$1/g' | grep '^<id>' | sed -e 's/$/<\/id>/'

sed基本上是面向行的,并且很难执行包含换行符的替换。另一方面,tr基本上是面向字符的。如果我们使用perl在战略位置插入换行符,那么我们可以过滤出以<id>开头的行,并再次添加匹配的</id>

使用xmllint --format也是一种很好的低复杂度方法,可以将xml转换为漂亮的xml,如果你不能正确地获得xpath查询,那么使用面向行的工具更容易分开。

$ cat input.xml | xmllint --format - | grep '^\s*<id>'

答案 1 :(得分:1)

sed它看起来像这样......

echo "$STRING" | sed 's/<\/id>.*<id>/<\/id><id>/;s/<mobile_device><general>//;s/<device_.*_device>//;'

输出看起来像这样......

<id>15</id><id>16</id>

如何运作
</id><id>之间的所有内容都会通过sed 's/<\/id>.*<id>/<\/id><id>/'删除。

然后通过<mobile_device>重新发送<general>sed 's/<mobile_device><general>//'

最后但并非最不重要的是<device_name ... mobile_device>之间的所有内容都会通过sed 's/<device_.*_device>//'删除。

希望这有帮助。

答案 2 :(得分:0)

您的sed字符串看起来已接近正常工作,以下是一些调整:

sed 's=.*\(<id>.*</id>\).*=\1='
  • 您需要选择一个未出现在命令表达式中的分隔符。 /用于关闭</id>,因此我使用了&#39; =&#39;代替。

  • 然后*将正在进行的正则表达式修改为&#34; 0或更多&#34;。你有一个>,这意味着&#39; 0或更多关闭括号&#39;。 .表示任何单个字符,是您真正应该使用的字符,因此带括号的表达式现在应与整个<id>字段匹配。

  • 最后,\1表示您希望将第一个带括号的子表达式的结果放在结果字符串中。

这对于一般解决方案有一些限制,但如果您知道每行只有一个ID字段,则应该提供。

答案 3 :(得分:0)

awk中的另一个人。将RSORS定义为>,并在标记<id</id之间进行阅读:

$ awk 'BEGIN{RS=ORS=">"} /<id/,/<\/id/' file
<id>15</id><id>16</id>$

由于ORS>,您需要使用printf手动添加最终换行符:

$ awk 'BEGIN{RS=ORS=">"} /<id/,/<\/id/; END{printf "\n"}' file
<id>15</id><id>16</id>
$

答案 4 :(得分:0)

gawk可以更简单一些:

awk '{print RT}' RS='<id>[^>]+>'

答案 5 :(得分:-1)

如果您有gawk

$ awk -v RS='</?id>' -v ORS='' '!(NR%2) {print pRT $0 RT} 
                                        {pRT=RT} 
                                 END    {printf "\n"}' file

当然,您可以对打印语句中的标签进行硬编码并删除RT。

相关问题