menesesinc
menesesinc

Reputation: 13

sed - range match - one side of the range is a multiline pattern

First time caller... Longtime reader.

Here is an example of the input that I am working with.

.
.
.
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"
.
.
.

I need to start my range at set vsys "datacenter" (for example) and stop when I see (2) consecutive exit.

Usually, I just:

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

But I need pattern #2 to catch (2) consecutive exit:

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

Thus my range match has a multiline pattern for range #2.

I've tried using the sed N command but I'm not doing something right. I will also use this sed command to parse out the vsys sections as needed and these sections always end with (2) consecutive exit.

As requested, the expected output should look like this:

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

Upvotes: 1

Views: 152

Answers (3)

John1024
John1024

Reputation: 113844

Using sed:

With GNU sed:

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

For BSD sed, try replacing -r with -E.

How it works

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

    This is a sed idiom for reading the whole file in at once. If the file is huge, the use a different solution, such as the awk solution below.

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

    This extracts the part that we want.

Using awk

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

How it works

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

    When we see the start line, set flag f to true (1).

  • f{print}

    If flag f is true print the line

  • /exit/ && last {exit;}

    If this line contains exit and the previous line also contained exit, then we are done and we tell awk to exit.

  • {last=0}

    Set last to false (0).

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

    This prepares for the next line. If flag f is true and this line contains exit, then set last to true (1).

Using 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

The logic here is basically the same as for the awk solution.

Upvotes: 1

potong
potong

Reputation: 58430

This might work for you (GNU sed):

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

If a line doesn't contain the required string delete it. If it does, append the following line and check for the double exit condition and if not found continue appending lines until the condition is met. Print out the lines held in the pattern space.

Upvotes: 0

Ed Morton
Ed Morton

Reputation: 203607

Untested since you didn't provide expected output but you need something like this:

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

Upvotes: 1

Related Questions