sed在两个模式之间,结果包含第三个模式

时间:2018-07-02 13:33:29

标签: bash sed

我正在尝试为xml响应过滤日志文件,并使用sed如下所示,查找所有xml消息相当容易。

sed -n '/<element/,/<\/element/p' file

返回:

<element>
    <id>12345</id>
    ...
</element>
<element>
    <id>54321</id>
    ...
</element>

但是,我一直无法弄清楚如何应用第二个过滤器,这意味着仅返回包含特定模式(例如ID)的xml响应。

在上面的示例中,如何过滤ID以仅返回第一个?

3 个答案:

答案 0 :(得分:2)

您可以将范围的命令分组:

sed -n '/<element/,/<\/element/{ /id/p }'

但是,在处理XML时,您应该真正考虑使用XML工具,例如xmlstarlet

要在搜索特定ID时打印完整的条目,如果到达了的结束标记,则需要使用保持空间<element>节点内累积行。一个<element>节点,您可以替换 hold 模式空格,匹配您的ID并打印:

sed -n -e '
  /<element/,/<\/element/H # append to the hold space
  /<\/element/{ 
    g  # replace pattern space with hold space
    /<id>12345<\/id>/p  # print if matching ID
    s/.*//  # clear pattern space
    x  # clear hold space
    b  # start next cycle without further output
}' input-file

你看,这真的很快变得很混乱。

答案 1 :(得分:2)

sed用于做s / old / new / 一无所有。在1980年代中期awk被发明时,所有古怪的单字符符文语言结构都被淘汰了。

$ cat tst.awk
/<element>/ { inElt = 1 }
inElt {
    elt = (elt == "" ? "" : elt ORS) $0
    if ( /<\/element>/ ) {
        if ( elt ~ /<id>12345<\/id>/ ) {
            print elt
        }
        elt = ""
        inElt = 0
    }
    next
}
{ print }

$ awk -f tst.awk file
<element>
    <id>12345</id>
    ...
</element>

与当前接受的sed解决方案相比,上述方法的主要优点是:

  1. 不需要您为</element测试两次
  2. 它可以像在任何UNIX盒子上的任何shell中使用任何awk一样工作,而不仅仅是使用某些sed
  3. 可以对其进行简单地增强,以添加其他条件和/或不同条件来打印(或不打印)相关元素
  4. 它不依赖任何神秘的单字符命令字符,所有内容都使用许多现代语言通用的基于algol的语法进行了清晰布置。

例如,假设您要打印文件中的第一个元素,而不管其ID是什么,而不是包含特定ID的元素。这将是上面的琐碎调整:

$ cat tst.awk
/<element>/ { inElt = 1 }
inElt {
    elt = (elt == "" ? "" : elt ORS) $0
    if ( /<\/element>/ ) {
        if ( ++cnt == 1 ) {
            print elt
        }
        elt = ""
        inElt = 0
    }
    next
}
{ print }

$ awk -f tst.awk file
<element>
    <id>12345</id>
    ...
</element>

如果要打印27号而不是1号元素,只需将++cnt == 1更改为++cnt == 27。尝试修改sed脚本以进行如此琐碎的需求更改,您可以期待完全的重写,并且必须调用其他工具。是否要打印多个元素和/或文件的其他部分不在元素标签内?与awk也绝对无关紧要。希望你明白了。

答案 2 :(得分:0)

这可能对您有用(GNU sed):

sed -n '/<element>/{:a;/<\/element>/!{N;ba};/<id>12345<\/id>/p}' file

通过使用-n选项来使用类似seds grep的性质,该选项可以关闭每行的自动打印。遇到包含<element>的行时,收集一系列行,直到到达结束标记</element>。现在检查<id>12345</id>的集合,如果为true,则打印该集合,否则该集合将被传递。

如果相反,您想要特定的元素,例如第二,使用:

sed -n '/<element>/{:a;/<\/element>/!{N;ba};x;s/^/x/;/^x\{2\}$/{x;p;b};x}' file

这将使用保存在保存空间中的计数器,该计数器在每个完整集合中都会递增,并检查一个特定的数字。

范围运算符,可以用作触发器类型的命令,但通常start address{:a;N;end address!ba; commands on collection}更为有用。