dawg
dawg

Reputation: 103714

sed print between two line patterns only if both patterns are found

Suppose I have a file with:

Line 1
Line 2
Start Line 3
Line 4 
Line 5
Line 6
End  Line 7
Line 8
Line 9
Start Line 10
Line 11
End Line 12
Line 13
Start line 14
Line 15

I want to use sed to print between the patterns only if both /Start/ and /End/ are found.

sed -n '/Start/,/End/p' works as expected if you know both markers are there and in the order expected, but it just prints from Start to the end of the file if End is either out of order or not present. (i.e., prints line 14 and line 15 in the example)

I have tried:

sed -n '/Start/,/End/{H;}; /End/{x; p;}' file

Prints:

# blank line here...
Start Line 3
Line 4 
Line 5
Line 6
End  Line 7
End  Line 7
Start Line 10
Line 11
End Line 12

which is close but two issues:

  1. Unwanted leading blank line
  2. End Line 7 printed twice

I am hoping for a result similar to

$ awk '/Start/{x=1} x{buf=buf$0"\n"} /End/{print buf; buf=""; x=0}' file
Start Line 3
Line 4 
Line 5
Line 6
End  Line 7

Start Line 10
Line 11
End Line 12

(blank lines between the blocks not necessary...)

Upvotes: 2

Views: 128

Answers (4)

anubhava
anubhava

Reputation: 784918

You can use this awk:

awk 'x{buf=buf ORS $0} /Start/{x=1; buf=$0} /End/{print buf; buf=""; x=0}' file

Start Line 3
Line 4
Line 5
Line 6
End  Line 7
Start Line 10
Line 11
End Line 12

Here is a sed version to do the same on OSX (BSD) sed (Based on Benjamin's sed command):

sed -n -e '/Start/{:a;' -e 'N;/End/!ba;' -e 'p;}' file

Upvotes: 1

Cyrus
Cyrus

Reputation: 88553

With GNU sed and sed from Solaris 11:

sed -n '/Start/{h;b;};H;/End/{g;p;}' file

Output:

Start Line 3
Line 4 
Line 5
Line 6
End  Line 7
Start Line 10
Line 11
End Line 12

If Start is found copy current pattern space to hold space (h) and branch to end of script (b). For every other line append current pattern space to hold space (H). If End is found copy hold space back to pattern space (g) and then print pattern space (p).

Upvotes: 2

Benjamin W.
Benjamin W.

Reputation: 52112

GNU sed: after encountering Start, keep appending lines as long as we don't see End; once we do, print the pattern space and start over:

$ sed -n '/Start/{:a;N;/End/!ba;p}' infile
Start Line 3
Line 4 
Line 5
Line 6
End  Line 7
Start Line 10
Line 11
End Line 12

Getting the newline between blocks is tricky. This would add one after each block, but results in an extra blank at the end:

$ sed -n '/Start/{:a;N;/End/!ba;s/$/\n/p}' infile
Start Line 3
Line 4 
Line 5
Line 6
End  Line 7

Start Line 10
Line 11
End Line 12
[blank]

Upvotes: 1

William Pursell
William Pursell

Reputation: 212198

Personally, I prefer your awk solution, but:

  sed -n -e '/start/,/end/H' -e '/end/{s/.*//; x; p}' input

Upvotes: 0

Related Questions