648

I need something like:

grep ^"unwanted_word"XXXXXXXX
fedorqui
  • 275,237
  • 103
  • 548
  • 598
john
  • 6,515
  • 3
  • 15
  • 4

9 Answers9

1110

You can do it using -v (for --invert-match) option of grep as:

grep -v "unwanted_word" file | grep XXXXXXXX

grep -v "unwanted_word" file will filter the lines that have the unwanted_word and grep XXXXXXXX will list only lines with pattern XXXXXXXX.

EDIT:

From your comment it looks like you want to list all lines without the unwanted_word. In that case all you need is:

grep -v 'unwanted_word' file
normanius
  • 8,629
  • 7
  • 53
  • 83
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • 2
    what if I want to exclude N lines after the line with "unwanted word" as well? `-v 'unwanted_word' --after N` doesn't help because it INCLUDES the line and N lines after. – Andrey Regentov Nov 06 '14 at 05:01
  • 1
    `-v` or `--invert-match` select non-matching lines. In your case `grep -v 'unwanted_word' file` or `grep --invert-match 'unwanted_word' file`. – adamski.pro Nov 29 '16 at 07:10
  • I want to ignore one line above and one line below with matching pattern then How can i achieve it? – Kanji Viroja Dec 20 '16 at 10:38
  • Awesome, I use this in git to quickly peruse the status of my repo, works like a charm: `git status -s |grep -v "folder_I_dont_care"` – benjaminz Jan 06 '17 at 16:12
  • 4
    Weird, it's the top answer, but in some cases it's wrong! If I want to find `sun`, except when it is `sunrise`, `grep sun|grep -v sunrise` skips line that contain both `sun` and `sunrise` at once, that is not what I want. `grep -P 'sun(?!rise)'` is much better. – greene Mar 24 '18 at 10:05
  • @greene provided the key - excluding the term *in the same query*. I'm using `ag`, not `grep` per se, and I can't pipe one `ag` to another. What I needed was `(?!foo)` - thanks! – dwanderson Jun 18 '18 at 19:05
  • 1
    If you want to bring the regex power into the exclude pattern, just add `-E`. e.g. `grep -v -E "unwanted_pattern_in_regex" file` – allenyllee Sep 28 '18 at 04:03
122

I understood the question as "How do I match a word but exclude another", for which one solution is two greps in series: First grep finding the wanted "word1", second grep excluding "word2":

grep "word1" | grep -v "word2"

In my case: I need to differentiate between "plot" and "#plot" which grep's "word" option won't do ("#" not being a alphanumerical).

starball
  • 20,030
  • 7
  • 43
  • 238
JPGConnolly
  • 1,320
  • 1
  • 8
  • 5
  • 26
    You should reverse the order to get highlighting on `word1`. – Matthew Read Jun 16 '15 at 20:45
  • 2
    I guess it would clarify to add a placeholder for the file name to that example – patrick Aug 11 '18 at 21:57
  • @MatthewRead I find it really more logic like this. First you're looking for occurences of "word1" then remove occurences found where there is also "word2" The opposite is strange : first removing "word2" and then looking the word you want. Maybe it's just a point of view – Nico Apr 07 '21 at 22:19
  • @Nico There's no reason to continue sticking to you initial impulse after finding something more useful, though. If you use this a lot, I would recommend creating a shell function that you can call (like `xnoty() { grep -v "$2" | grep "$1" }`) so you don't have to remember the construction. – Matthew Read Apr 08 '21 at 16:16
46

The right solution is to use grep -v "word" file, with its awk equivalent:

awk '!/word/' file

However, if you happen to have a more complex situation in which you want, say, XXX to appear and YYY not to appear, then awk comes handy instead of piping several greps:

awk '/XXX/ && !/YYY/' file
#    ^^^^^    ^^^^^^
# I want it      |
#            I don't want it

You can even say something more complex. For example: I want those lines containing either XXX or YYY, but not ZZZ:

awk '(/XXX/ || /YYY/) && !/ZZZ/' file

etc.

fedorqui
  • 275,237
  • 103
  • 548
  • 598
  • 2
    It appears to be much faster than the `grep -P` solution on big files. – MBR May 13 '16 at 15:15
  • @MBR `grep -P` means using Perl regexp, so loading that package is going to be way more expensive than a normal `grep`. – fedorqui May 14 '16 at 22:28
46

If your grep supports Perl regular expression with -P option you can do (if bash; if tcsh you'll need to escape the !):

grep -P '(?!.*unwanted_word)keyword' file

Demo:

$ cat file
foo1
foo2
foo3
foo4
bar
baz

Let us now list all foo except foo3

$ grep -P '(?!.*foo3)foo' file
foo1
foo2
foo4
$ 
amos
  • 5,092
  • 4
  • 34
  • 43
codaddict
  • 445,704
  • 82
  • 492
  • 529
  • Thanks for this, very useful! I would like to mention that The grep command is case sensitive by default – DocWiki Jul 16 '11 at 17:49
  • 3
    Note that `grep -v -P` also works without negation in regular expression. – cybersoft Oct 29 '15 at 09:23
  • **"if bash...you'll need to escape the `!`"**. Thank you thank you thank you! That's what I wanted! – Gabriel Staples Apr 19 '20 at 06:55
  • However, this doesn't work in a way of `grep -P '(?!.*foo3)[a-zA-Z0-9]*' pattern, it won't find what you want to omit, but will find only the exact thing, so regexp is little useless for an exact phrases – FantomX1 Oct 22 '20 at 14:19
  • 1
    The proposed pattern `(?!.*unwanted_word)keyword` only excludes lines where the `unwanted_word` starts *after* the `keyword` (possibly overlapped). To exclude *any* line that contains the `unwanted_word`, regardless of its position relative to the `keyword`, use `^(?!.*unwanted_word).*\Kkeyword` . – Maëlan Jan 16 '22 at 18:15
13

Invert match using grep -v:

grep -v "unwanted word" file pattern
garima
  • 5,154
  • 11
  • 46
  • 77
7

grep provides '-v' or '--invert-match' option to select non-matching lines.

e.g.

grep -v 'unwanted_pattern' file_name

This will output all the lines from file file_name, which does not have 'unwanted_pattern'.

If you are searching the pattern in multiple files inside a folder, you can use the recursive search option as follows

grep -r 'wanted_pattern' * | grep -v 'unwanted_pattern'

Here grep will try to list all the occurrences of 'wanted_pattern' in all the files from within currently directory and pass it to second grep to filter out the 'unwanted_pattern'. '|' - pipe will tell shell to connect the standard output of left program (grep -r 'wanted_pattern' *) to standard input of right program (grep -v 'unwanted_pattern').

Shriganesh Shintre
  • 2,428
  • 3
  • 17
  • 16
5

The -v option will show you all the lines that don't match the pattern.

grep -v ^unwanted_word
st0le
  • 33,375
  • 8
  • 89
  • 89
0

I excluded the root ("/") mount point by using grep -vw "^/".

# cat /tmp/topfsfind.txt| head -4 |awk '{print $NF}'
/
/root/.m2
/root
/var

# cat /tmp/topfsfind.txt| head -4 |awk '{print $NF}' | grep -vw "^/"
/root/.m2
/root
/var
Worthwelle
  • 1,244
  • 1
  • 16
  • 19
-4

I've a directory with a bunch of files. I want to find all the files that DO NOT contain the string "speedup" so I successfully used the following command:

grep -iL speedup *
bobble bubble
  • 16,888
  • 3
  • 27
  • 46
pjrieger
  • 11
  • 1
  • 1
    From the man page: "-L, --files-without-match Suppress normal output; instead print the name of each input file from which no output would normally have been printed. **The scanning will stop on the first match.**" (Emphasis by me) So beware of this! – xuiqzy Jun 07 '16 at 22:45