2

Similar to, but not exactly like How to select first occurrence between two patterns including them... given this input file:

something P1 something
content1
content2
something P1 something
content3
content4

I need just this output:

something P1 something
content1
content2
9
  • 3
    sed -n '/something P1 something/,/something P1 something/p' input | head -n -1
    – jesse_b
    Commented Jan 4, 2020 at 18:40
  • 1
    @Jesse_b make your comment an answer, and I'll upvote it.
    – RonJohn
    Commented Jan 4, 2020 at 18:48
  • @RonJohn Is it guaranteed that at least two lines matching the pattern will appear?
    – Torin
    Commented Jan 4, 2020 at 18:49
  • @Torin yes, I think so. An answer that doesn't rely on head -n -1 would be useful, though just in case.
    – RonJohn
    Commented Jan 4, 2020 at 18:50
  • 1
    @EdMorton you could have said that in the first place. Partial matching on P1 is what I really need, and the Torin answer (which exactly answers my question) was easily modified to only match strings with P1 in them.
    – RonJohn
    Commented Jan 4, 2020 at 19:24

4 Answers 4

4

An awk solution:

 awk '/^something P1 something$/{if(++i>1)exit} i' input_file

This will print the first line matching /^something P1 something$/ and all lines until either the next line matching that pattern (but not including said line) or the end of file.

3

This is what I suspect you really want:

To print the first block:

$ awk '$0=="something P1 something"{c++} c==1' file
something P1 something
content1
content2

or to print the 2nd:

$ awk '$0=="something P1 something"{c++} c==2' file
something P1 something
content3
content4

and so on. Without a clear statement of requirements it's just a guess though.

2
  • 1
    My question clearly specifies that the output must contain content1 and content2, so it's the first block I want. However, knowing how to also get the second block is also useful (though outside the scope of the question.
    – RonJohn
    Commented Jan 4, 2020 at 19:07
  • Right so I showed you the idiomatic solution that does what you specifically asked for and can be used for other purposes in future.
    – Ed Morton
    Commented Jan 4, 2020 at 19:09
2
 $ sed -ne '
     /P1/!d
     :loop
        p;n
     //!bloop
     q
 ' file 

Results:

something P1 something1
content1
content2

Using the Gnu sed editor with non Posix construct Q :

$ sed -e '
   /P1/,/P1/!d
   //!{$q;b;}
   G;/\n./Q;s/\n.*//;h
' file

With Posix only constructs we do this:

 $ sed -ne '
      /P1/,/P1/!d
      //!{
        p;$q;d
      }
      G;/\n./q;s/\n.*//p;h
 ' file

With Perl :

$ perl -lne '
    next unless $e = /P1/ ... /P1/;
    $e =~ /E/ ? last : print;
' file

Yet another:

$ perl -0777 -pe '$_ = /^(.*?P1(?s:.*?\n))(?=.*?P1)/m ? $1 : $,' file
2

awk

A general solution for ith pattern block in awk is:

awk -v i=1 -v pat='something P1 something'    '$0~pat{i--}i==0'

Explanation:

-v i=1        # sets the pattern block to print (1 in this case).
-v pat='...'  # sets the regex pattern that will be tested.

$0~pat        # tests if the input line match the pattern
{i--}         # If the pattern was found, reduce the count.
i==0          # If the count has reduced to 0, print the block lines.

If the pattern that matters is only P1, then use:

awk -v i=1 -v pat='P1' '$0~pat{i--}i==0'

For a faster execution, exit when the block has ended:

awk -v i=1 -v pat='P1' '$0~pat{i--}i==0;i<0{exit}'

If you want a literal match (not a pattern), use:

awk -v i=1 -v pat='P1' '$0 == pat {i--}; i==0; i<0{exit}'

sed

To get from the first instance of one pattern to the next instance of a pattern, you can do in GNU sed:

sed -n '/something P1 something/!b;b2;:1;{/something P1 something/q;:2;p;n;b1}'

There may be some lines before the first something P1 something.
The script stops (fast) when the second pattern is found.

As both patterns (start and end) are equal, we may reduce the command to:

sed -n -e '/something P1 something/!b;b2;:1;{//q;:2;p;n;b1}'

And to make it more portable, use:

sed -n -e '/something P1 something/!{b' -e '};b2' -e ':1' -e '{//q;:2' -e 'p;n;b1' -e '}'
0

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .