4

How can I display file content from the beginning up to some multi-line pattern without including the pattern itself?

For example, if I had a text file like this:

cat
dog
fox
cow
dove
bird
bunny
gnu
hen
dove
bird
buffalo

and if my pattern was this:

dove
bird
bunny

what I'd like to get would be:

cat
dog
fox
cow

My real file is huge so if there are multiple ways to achieve this, I'd prefer faster ways.

Also, I asked a similar question related to this just now, but it's different so please don't mark this as duplicate just because of that!

  • 1
    If the pattern is not found anywhere within the file should it print everything, or nothing? – steeldriver Jun 21 '17 at 19:05
  • @steeldriver, hopefully I'd like it to give an error instead in that kind of situation, but my file basically always has the pattern so it doesn't really matter. – stacko Jun 21 '17 at 19:17
  • sed -n '1N;N;/dove\nbird\bunny/q;P;D' will hold 3 lines in the pattern space at any point in time. Then we print the leftmost, slide it out, append next... till we hit the multi-line pat, at which point we promptly quit. Since the pattern is sure to be found, hence no need for storage. – user218374 Jun 24 '17 at 4:22
1

You could handle the input line-wise with sed and chain the multi-line pattern match:

/pat1/ { N; N; ...; /pat2\npat3\n.../q }

So in your example, that would be e.g.:

sed -n '/^dove$/ { N; N; /\nbird\nbunny$/q; }; p' infile

Output:

cat
dog
fox
cow
| improve this answer | |
1

Here's an alternate sed approach, that uses branching to maintain a 3-line buffer in pattern space and test it against the multiline pattern, quitting when found:

sed -n '
  :a
  $q
  N
  /dove\nbird\nbunny/q
  3,$ {
    P
    D
  }
  ba
' file

With GNU sed, you can use the Q command to quit without printing the current pattern space, plus it allows a more free-form syntax e.g.

sed -e :a -e '$Q' -e 'N;/dove\nbird\nbunny/Q' -e '3,${P;D}' -e 'ba' file

or

sed ':a; $Q; N; /dove\nbird\nbunny/Q; 3,${P;D}; ba' file
| improve this answer | |
0

Here is a bit of a Python hack to do that.

Code:

# !/usr/bin/python
import sys
with open(sys.argv[2], 'rU') as f:
    patterns = f.readlines()

result = None
with open(sys.argv[3], 'rU') as f:
    last_match = 0
    for i, line in enumerate(f):
        if line != patterns[last_match]:
            last_match = 0
        else:
            last_match += 1
            if last_match == len(patterns):
                result = i + 1 - len(patterns)
                break

if result != None:
    if sys.argv[1] == 'tail':
        print("tail -n+%d %s" % (result+1, sys.argv[3]))
    else:
        print("head -n%d %s" % (result, sys.argv[3]))

Run it

To output up to the pattern:

`python split_on_pattern.py head pattern_file data_file`

To output from the pattern on:

`python split_on_pattern.py tail pattern_file data_file`
| improve this answer | |

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.