【问题标题】:sed - range match - one side of the range is a multiline patternsed - 范围匹配 - 范围的一侧是多行模式
【发布时间】:2015-07-01 03:53:46
【问题描述】:

第一次来电...长期读者。

这是我正在使用的输入示例。

.
.
.
exit
exit
set vsys "dr-site"
set vsys-id 2
set vrouter "dr-site-vr"
unset auto-route-export
exit
set service "service1"
set service "service2"
set zone id 101 "Trust1"
set zone id 201 "Untrust1"
set zone id 301 "DMZ1"
set interface "ethernet3/1.3"
set interface "ethernet3/1.4"
set interface "ethernet3/1.6"
set route 0.0.0.0/0
set address "Untrust1"
set address "Trust1"
set address "DMZ1"
set group address "Untrust1"
set group address "Trust1"
set group address "DMZ1"
set group service
set policy id 100
exit
set policy id 200
exit
set policy id 300
exit
set policy id 400
exit
exit
set vsys "datacenter"
set vsys-id 1
set vrouter "datacenter-vr"
unset auto-route-export
exit
set service "service1"
set service "service2"
set zone id 100 "Trust"
set zone id 200 "Untrust"
set zone id 300 "DMZ"
set interface "ethernet3/1.5"
set interface "ethernet3/1.2"
set interface "ethernet3/1.1"
set route 0.0.0.0/0
set address "Untrust"
set address "Trust"
set address "DMZ"
set group address "Untrust"
set group address "Trust"
set group address "DMZ"
set group service
set policy id 100
exit
set policy id 200
exit
set policy id 300
exit
set policy id 400
exit
exit
set vsys "other"
.
.
.

我需要从set vsys "datacenter"(例如)开始我的范围,当我看到(2)个连续的exit时停止。

通常,我只是:

gsed -n '/set vsys "datacenter"/,/exit/p; file

但我需要模式 #2 来捕捉 (2) 连续退出:

gsed -n '/set vsys "datacenter"/,/exit\nexit/p; file

因此,我的范围匹配对范围 #2 有一个多行模式。

我尝试过使用 sed N 命令,但我做的不对。我还将根据需要使用此 sed 命令解析出 vsys 部分,这些部分始终以 (2) 个连续的 exit 结尾。

根据要求,预期输出应如下所示:

set vsys "datacenter"
set vsys-id 1
set vrouter "datacenter-vr"
unset auto-route-export
exit
set service "service1"
set service "service2"
set zone id 100 "Trust"
set zone id 200 "Untrust"
set zone id 300 "DMZ"
set interface "ethernet3/1.5"
set interface "ethernet3/1.2"
set interface "ethernet3/1.1"
set route 0.0.0.0/0
set address "Untrust"
set address "Trust"
set address "DMZ"
set group address "Untrust"
set group address "Trust"
set group address "DMZ"
set group service
set policy id 100
exit
set policy id 200
exit
set policy id 300
exit
set policy id 400
exit
exit

【问题讨论】:

  • 显示给定输入的预期输出。 sed 用于在单独的行上进行简单的替换——这个问题的最简单的解决方案是在 awk 中。

标签: awk sed pattern-matching range


【解决方案1】:

使用 sed:

使用 GNU sed:

sed -r 'H;1h;$!d;x;s/.*(set vsys "datacenter".*exit\nexit).*/\1/' file

对于 BSD sed,尝试将 -r 替换为 -E

工作原理

  • H;1h;$!d;x;

    这是一次读取整个文件的 sed 习惯用法。如果文件很大,请使用不同的解决方案,例如下面的 awk 解决方案。

  • s/.*(set vsys "datacenter".*exit\nexit).*/\1/

    这会提取我们想要的部分。

使用 awk

awk '/set vsys "datacenter"/{f=1;} f{print;} /exit/ && last {exit;} {last=0;} f && /exit/{last=1;}' file

工作原理

  • /set vsys "datacenter"/{f=1;}

    当我们看到起始行时,将标志 f 设置为 true (1)。

  • f{print}

    如果标志 f 为真,则打印该行

  • /exit/ && last {exit;}

    如果这一行包含exit,而上一行也包含exit,那么我们就完成了,我们将awk 告诉exit

  • {last=0}

    将 last 设置为 false (0)。

  • f && /exit/{last=1}

    这为下一行做准备。如果标志f 为真并且此行包含exit,则将last 设置为真(1)。

使用python3

#!/usr/bin/python3
f = False
last = False
with open('file', 'r') as fhandle:
    for line in fhandle:
        if 'set vsys "datacenter"' in line:
            f = True
        if f:
            print(line, end="")
        if last and 'exit' in line:
            break
        last = False
        if f and 'exit' in line:
            last = True

这里的逻辑与awk方案基本相同。

【讨论】:

  • 感谢您添加带有解释的 sed 以及 awk 和解释。您的 sed 和 awk 都按要求工作。我希望在 sed 中有一种“更漂亮”的方式。我想一旦我开始使用多行代码,我应该研究一下 Perl 或 Python。 =_)
  • @menesesinc 为了比较,我添加了一个python解决方案。
  • 当我使用 32 个“vsys”时,第一个使用您的 gsed 脚本工作,但公平地说,它是配置中的最后一个 vsys。今天,我不得不移动到同一个大文件中的另一个 vsys 并且 gsed 脚本正在捕获,使用我原来的示例,设置 vsys“dr-site”和介于两者之间的所有内容,通过它的 exit\nexit,然后捕获 set vsys“数据中心”以及介于两者之间的所有内容,最后以该 vsys 的退出\nexit 结束。不过,您的 awk 脚本可以完美地处理这两种情况。
【解决方案2】:

未经测试,因为您没有提供预期的输出,但您需要这样的东西:

awk '
/set vsys "datacenter"/{ f=1; rec=prev="" }
f {
    rec=rec $0 ORS
    if ( /exit/ && (prev==$0) ) {
        printf "%s", rec
        f=0
    }
    prev = $0
}
' file

【讨论】:

    【解决方案3】:

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

    sed '/set vsys "datacenter"/!d;:a;N;/exit\s*\nexit\s*$/!ba' file
    

    如果一行不包含所需的字符串,请将其删除。如果是,请附加以下行并检查双重exit 条件,如果未找到,则继续附加行,直到满足条件。打印出模式空间中的行。

    【讨论】:

    • 这也很有帮助。感谢您添加评论。
    猜你喜欢
    • 2011-08-08
    • 1970-01-01
    • 2021-03-15
    • 1970-01-01
    • 1970-01-01
    • 2012-12-27
    • 1970-01-01
    • 1970-01-01
    • 2011-07-17
    相关资源
    最近更新 更多