112

grep --before-context 5 shows 5 lines before the match.

I want to show everything before the match.
Doing grep --before-context 99999999 would work but it is not very... professional.

How to show all the file up to the match?

8 Answers 8

157

Sed is better for that.

Just do:

sed '/PATTERN/q' FILE

It works like this:

For each line, we look if it matches /PATTERN:

  • if yes, we print it and quit
  • otherwise, we print it

This is the most efficient solution, because as soon as it sees PATTERN, it quits. Without q, sed would continue to read the rest of the file, and do nothing with it. For big files it can make a difference.

This trick can also be used to emulate head:

sed 10q FILE
5
  • Just tried, it just outputs the first line of the file... even though match is at line 38. Apr 14, 2011 at 9:44
  • Works fine for me. Can you give an example of actual input and output? And the command you are running as-is.
    – Mikel
    Apr 14, 2011 at 9:46
  • I had tried your command before you edited it, it was: sed '/PATTERN/p;q' FILE Apr 14, 2011 at 9:51
  • 12
    What should I do, if I don't want to print the line with the pattern match? Jul 6, 2017 at 12:29
  • 14
    @tommy.carstensen: sed '/PATTERN/Q' FILE will skip the matched line. Q is a GNU extension, so it won't work with any sed.
    – Alex O
    Sep 8, 2018 at 13:37
56

print up to and including the match:

awk '{print} /pattern/ {exit}' filename
sed '/pattern/q' filename

print up to BUT NOT including the match:

awk '/pattern/ {exit} {print}' filename
sed '/pattern/Q' filename
5
  • 16
    Q's cool but gnu specific afaik, sed -n '/pattern/!p;//q' would be more portable. Apr 6, 2015 at 13:36
  • @don_crissti: you should make it an answer, I think it's a good one (: I am a little curious how it works, though. I believe ! makes p apply to lines not matching pattern, but then the //q confuses me...
    – jwd
    Aug 22, 2019 at 1:25
  • 2
    @don_crissti: ah, I got it — // means "previous regular expression" (I thought it meant "match the empty string"). I think a shorter version of the same solution is: sed -n '/pattern/q;p ?
    – jwd
    Aug 22, 2019 at 1:39
  • @jwd - indeed, that's shorter. 👍 Aug 22, 2019 at 19:30
  • @don_crissti 's answer comment did not work for me. I am using a CentOS release 6.8 (Final) machine with tcsh shell v6.17. However, Jim Grisham's answer: unix.stackexchange.com/a/410506/346011 in the post script worked: sed '/PATTERN/q' FILE | sed '$d'
    – ZeZNiQ
    Sep 19, 2021 at 1:36
43

sed can replace most of grep's functionality.

sed -n '1,/<pattern>/ p' <file>

This means print from the first line until pattern is matched.

A couple of range examples

sed -n '/<pattern>/,$ p' <file> # from pattern to end of file
sed -n '/<pattern1>/,/<pattern2>/ p' <file> # from pattern1 to pattern2
2
  • 3
    This command is good, but you can do better. This way it reads the whole file, but it is possible to quit as soon as it has found a match.
    – Mikel
    Apr 14, 2011 at 9:55
  • 5
    What should I do, if I don't want to print the line with the pattern match? Jul 6, 2017 at 12:29
3

Adding onto Mikel's answer above...


To print all lines up to, but not including, the first line in FILE containing PATTERN, try:

  • sed '/.*PATTERN.*/{s///;q;}' FILE

This matches the entire line containing the pattern, replaces it with a blank line, then quits without processing the rest of the file.


Post-script:

The easiest/clearest way I could think of to prevent printing an extra newline at the end (without involving another tool), was to run sed again and delete the new final line:

sed '/.*PATTERN.*/{s///;q;}' FILE | sed '$d'

... and since we're now deleting that line anyway, our previous work is redundant and we can simplify to:

sed '/PATTERN/q' FILE | sed '$d'
6
  • Glenn's answer - and my comment there - show how to do it with a single sed invocation. Dec 12, 2017 at 20:27
  • (Thanks for that - I did see your comment on agc's answer but either missed the other one or just skimmed it because my brain dislikes double-negatives.) Since I was using this in both a tcsh and a bash alias, I needed to make sure I had a relatively concise one-line solution that worked in both standard and GNU sed (for portability); all requirements that your contribution may very well have met. As someone who uses sed very rarely, my most important requirement was for something that I could quickly understand when I want to easily edit or re-purpose it years from now. Dec 14, 2017 at 19:02
  • 1
    @don_crissti 's answer comment did not work for me. I am using a CentOS release 6.8 (Final) with tcsh shell v6.17. However, sed '/PATTERN/q' FILE | sed '$d' worked. Thanks!
    – ZeZNiQ
    Sep 19, 2021 at 0:41
  • 1
    Never use .*PATTERN.* with grep or sed. Those tools look anywhere in the line anyway and this just makes things more complicate for the matching algorithm. In worst case it could be a lot slower than just PATTERN.
    – Thraidh
    Oct 4, 2021 at 15:12
  • 1
    @JimGrisham You are right. I should have been more specific and said 'Never use .*PATTERN.* for searching'. If you want to use it for purposes of replacement, then it is a acceptable. Anyway, if you want to replace a whole line which contains a specific pattern, it is probably better to use sed '/PATTERN/s/.*/new content/. Anyway, if PATTERN is simple, it does not matter. If it contains one or more .* itself, you'll have to start to be careful.
    – Thraidh
    Oct 6, 2021 at 12:25
3

For people who choose to remember only the basic tools in day to day work, and willing to accept less elegant and less efficient solutions:

head -n $(grep -n pattern filename | cut -d: -f1) filename

If this command is for a script then I will look for more elegant (and possibly efficient) solutions. If this is a one time command or a throw away script then I don't care.

2
  • 1
    Nice idea, but three commands when one will do.
    – Mikel
    Apr 14, 2011 at 9:47
  • 1
    Knowing the basics is very good indeed. Knowing the right tool for the job is better, though.
    – soulmerge
    Apr 14, 2011 at 15:00
1

The following pure GNU grep methods are not efficient.

Search for everything up to the first instance of string "foo" in file bar, using three greps:

grep -m 1 -B $(grep -n -m 1 foo bar | grep -o '^[0-9]*') foo bar

Matching up to the last instance of "foo":

grep -oPz "(?s)[^\n]*${s}.*?\n.*?foo.*?\n" bar

Note: details on the last grep can be found in: Regex (grep) for multi-line search needed.

3
  • Why would one ever want to use 7 greps (+ pcre) when it's a matter of simply running a single sed invocation: sed 'x;/./G;//!x;/foo/p;//s/.*//;x;d' ?? Oct 14, 2016 at 13:02
  • @don_crissti, your sed code seems worth its own answer, or could be added to one of the others. Re 7 greps: Because there was no grep answer... (plus, the answer helps show why not.)
    – agc
    Oct 14, 2016 at 18:21
  • It's not my code, just click on it... Even if that was my code it does not answer the Q here so I wouldn't post it as an answer. Oct 14, 2016 at 18:25
0

You could also use one of the following

tac ./test | grep -B $(cat ./test | wc -l) -m 1 'pattern'|tac 

or

tac ./test |head -n $(tac ./test | grep -n 'pattern' | cut -d: -f1 | head -n 1)|tac

or

tac ./test |sed ':a;N;$!ba;s/\n/'"pattern"'/g' | sed 's/'"patternpattern"'/\n/g'|head -n 1|sed 's/'"pattern"'/\n/g'|tac

The first option is very similar to what the OP suggested only it makes sure ti show enough lines before context by counting the lines in the file

The second option searches the line number of the first match (you could change that as well by changing the inner 'head') and than uses head on that number

The last option replaces all new lines with the match and than replaces two adjacent matches with a new line. The output of this is a line for every block of text between two matches. After that it uses 'head' to choose the first line (mwaning the block of text until the first match) match and than retranslates each match to a new line. this option works only if the file is in the following format

pattern
texttexttext
texttexttext texttexttext
texttexttexttexttexttexttexttexttext
pattern
texttexttext
pattern 
texttexttext
texttexttexttexttexttexttexttexttext

and so forth

2
  • 2
    consider explaining how these work, especially because that sed command at the bottom is kind of gnarly.
    – strugee
    Jul 9, 2015 at 19:45
  • the first option is very similar to what the OP suggested only it makes sure ti show enough kines before context by counting the lines in the filr,
    – user122778
    Jul 10, 2015 at 5:49
0
ruby -pe 'exit if /PATTERN/' file(s)

sed and alike is certainly preferable performance-wise. It can also be done as a Ruby one-liner with an order of magnitude performance penalty. This may be of interest if, in addition to stopping at a pattern, you want to do some processing that requires methods found in the standard Ruby library or a third-party Gem.

# Extract unique links from Markdown files
ruby -rcommonregex -ne \
  'puts $<.map{CommonRegex.get_links(_1)}.uniq.sort;
   exit if /PATTERN/' *.md

Aside from usefulness, its little known Ruby has adopted conventions found in awk or perl.

You must log in to answer this question.

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