22

How can I select first occurrence between two patterns including them. Preferably using sed or awk.

I have:

text
something P1 something
content1
content2
something P2 something
text
something P1 something
content3
content4
something P2 something
text

I want the first occurrence of the lines between P1 and P2 (including P1 line and P2 line):

something P1 something
content1
content2
something P2 something
19
sed '/P1/,/P2/!d;/P2/q'

...would do the job portably by deleting all lines which do !not fall within the range, then quitting the first time it encounters the end of the range. It does not fail for P2 preceding P1, and it does not require GNU specific syntax to write simply.

  • Excellent! Much better than mine. – muru Jan 23 '15 at 22:04
  • 1
    @muru - It is often easier to avoid contortions if you try to target the autoprint - let the cycle work for you. That's the habit I've fallen into anyway. I think it's probably best described as a prune vs a select method - I tend to wind up negating a pattern rather than searching for it. – mikeserv Jan 23 '15 at 22:26
  • This will hung when processing huge fileSize. – Brain90 Dec 13 '17 at 7:20
  • @Brain90 - shouldnt. if you can reliably reproduce your complaint you should address the maintainer of your sed... thats a bug in the sed youre running, and not in tbe script above. – mikeserv Aug 4 '18 at 22:39
8

with awk

awk '/P1/{a=1};a;/P2/{exit}' file
something P1 something
content1
content2
something P2 something
7

In sed:

sed -n '/P1/,/P2/p; /P2/q'
  • -n suppresses the default printing, and you print lines between the matching address ranges using the p command.
  • Normally this would match both the sections, so you quit (q) when the first P2 matches.

This will fail if a P2 comes before P1. To handle that case, try:

sed -n '/P1/,/P2/{p; /P2/q}'
  • I disagree;  mikeserv’s answer is not any better than yours. – G-Man Apr 16 at 18:24
1

If you want to skip the patterns themselves, here is the awk version:

awk '/P2/ {exit} /P1/ {f=1; next} f' file
  • Works for me. Could you add some more info about how the command works? – 0xAffe Apr 10 at 11:14
1

A simpler awk solution (sort-of halfway between iruvar’s answer and muru’s answer, but not using a variable):

awk '/P1/,/P2/ { print }  /P2/ { exit }'

and, as muru noted, if the first P2 appears before the first P1, this will print nothing.

Of course, if you want to print all the P1-P2 ranges:

something P1 something
content1
content2
something P2 something
something P1 something
content3
content4
something P2 something

just leave out the exit part:

awk '/P1/,/P2/ { print }'
1
awk '/P1/,/P2/{print;f=1} f&&/P2/{exit}' data

Quit immediately after print, not before.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.