Ida
Ida

Reputation: 2999

sed - print all lines before the nth occurrence of a string

Suppose I have:

content line 1
content line 2
blabla *my_pattern_str* (1st occurrence)
...
content line x 
blabla *my_pattern_str* (nth occurrence <- I want to print from the beginning line up to here)
content line y
content line y+1
...

I would like to print all the lines before and including the nth occurrence of my_pattern_str. How can I do this using sed (or similar command like grep or awk)?

Upvotes: 3

Views: 2885

Answers (4)

carl.anderson
carl.anderson

Reputation: 1118

This is horrible, but it does exactly what you asked for.

cat input_file.txt \
  | tr '\n' '\0' \
  | sed -e 's:my_pattern:my_pattern\
:g' \
  | head -n$X \
  | tr -d '\n' \
  | tr '\0' '\n'

Notice the awesome use of an escaped newline within the sed pattern - I'm not sure if this can be avoided.

The idea here is to:

  1. replace all newlines with \0
  2. insert a newline after each pattern match
  3. use head -n X to grab the first X matches
  4. remove the newlines you inserted after the matches
  5. replace the \0 characters with newlines
  6. (Optional) Cry in the shower.

Upvotes: 1

rici
rici

Reputation: 241671

Adjust 7 and my_pattern as necessary.

awk -v N=7 '{print}/my_pattern/&&--N<=0{exit}'

More cryptically, the following will also work:

awk -v N=7 '1;/my_pattern/&&--N<=0{exit}'

Both of the above actually stop on the Nth line containing the pattern, rather than the Nth occurrence of the pattern. If you want the Nth occurrence of the pattern:

awk -v N=7 -v M=my_pattern '1;(N-=gsub(M,""))<=0{exit}'

Eg:

printf %s\\n line1 "pattern in line 2" "pattern pattern in line 3" line4 pattern |
awk -v N=3 -v M=pattern '1;(N-=gsub(M,""))<=0{exit}'

=>

line1
pattern in line 2
pattern pattern in line 3

Upvotes: 2

Tom Fenech
Tom Fenech

Reputation: 74596

You could use this. The variable N is the max number of times. It will process the rest of the file but I don't think that's a big deal:

awk -vN=2 'n<N;/my_pattern/{++n}' file

Increment a counter every time the pattern is matched. Print the line as long as the counter is lower than the the variable N.

Upvotes: 7

konsolebox
konsolebox

Reputation: 75458

This would only print those lines if a pattern really exists along the way and not print everything if it's not found:

awk '{lines[NR]=$0}/pattern/{for(i=1;i<=NR;++i)print lines[i];exit}' file

Upvotes: 0

Related Questions