mmdanziger
mmdanziger

Reputation: 4658

Make grep stop after first NON-matching line

I'm trying to use grep to go through some logs and only select the most recent entries. The logs have years of heavy traffic on them so it's silly to do

    tac error.log | grep 2012
    tac error.log | grep "Jan.2012" 

etc.

and wait for 10 minutes while it goes through several million lines which I already know are not going to match. I know there is the -m option to stop at the first match but I don't know of a way to make it stop at first non-match. I could do something like grep -B MAX_INT -m 1 2011 but that's hardly an optimal solution.

Can grep handle this or would awk make more sense?

Upvotes: 8

Views: 2809

Answers (5)

Andreas Spindler
Andreas Spindler

Reputation: 8150

Here is an example that parses the user's dot-plan file and stops at the first non-matching line:

PID=$$
while read ln; do
  echo $ln | {
    if grep "^[-*+] " >/dev/null; then
      # matched
      echo -e $ln
    elif grep "^[#]" >/dev/null; then
      # ignore comment line
      :
    else
      # stop at first non-matching line
      kill $PID
    fi
  }
done <$HOME/.plan

Of course this approach is considerably slower than if Grep reads the lines but at least you can incorporate several cases (not just the non-match).

For more complex scripts, it is worth noting that Bash can also apply regular expressions to variables, i.e. you can also do completely without grep.

Upvotes: 0

guettli
guettli

Reputation: 27169

Here is a solution in python:

# foo.py
import sys, re
for line in sys.stdin:
    if re.match(r'2012', line):
        print line,
        continue
    break

you@host> tac foo.txt | python foo.py

Upvotes: 2

l0b0
l0b0

Reputation: 58998

The excellent one-line scripts for sed page to the rescue:

# print section of file between two regular expressions (inclusive)
sed -n '/Iowa/,/Montana/p'             # case sensitive

In other words, you should be able to do the following:

sed -n '/Jan 01 2012/,/Feb 01 2012/p' error.log | grep whatevs

Upvotes: 1

reinierpost
reinierpost

Reputation: 8611

I don't think grep supports this.

But here is my "why did we have awk again" answer:

tail -n `tac biglogfile | grep -vnm1 2012 | sed 's/:.*//' | xargs expr -1 +` biglogfile

Note that this isn't going to be exact if your log is being written to.

Upvotes: 1

dogbane
dogbane

Reputation: 274888

How about using awk like this:

tac error.log | awk '{if(/2012/)print;else exit}'

This should exit as soon as a line not matching 2012 is found.

Upvotes: 7

Related Questions