[bash] How to grep, excluding some patterns?

I'd like find lines in files with an occurrence of some pattern and an absence of some other pattern. For example, I need find all files/lines including loom except ones with gloom. So, I can find loom with command:

grep -n 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp)

Now, I want to search loom excluding gloom. However, both of following commands failed:

grep -v 'gloom' -n 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp)
grep -n 'loom' -v 'gloom' ~/projects/**/trunk/src/**/*.@(h|cpp)

What should I do to achieve my goal?

EDIT 1: I mean that loom and gloom are the character sequences (not necessarily the words). So, I need, for example, bloomberg in the command output and don't need ungloomy.

EDIT 2: There is sample of my expectations. Both of following lines are in command output:

I faced the icons that loomed through the veil of incense.

Arty is slooming in a gloomy day.

Both of following lines aren't in command output:

It’s gloomyin’ ower terrible — great muckle doolders o’ cloods.

In the south west round of the heigh pyntit hall

This question is related to bash grep

The answer is


/*You might be looking something like this?

grep -vn "gloom" `grep -l "loom" ~/projects/**/trunk/src/**/*.@(h|cpp)`

The BACKQUOTES are used like brackets for commands, so in this case with -l enabled, the code in the BACKQUOTES will return you the file names, then with -vn to do what you wanted: have filenames, linenumbers, and also the actual lines.

UPDATE Or with xargs

grep -l "loom" ~/projects/**/trunk/src/**/*.@(h|cpp) | xargs grep -vn "gloom"

Hope that helps.*/

Please ignore what I've written above, it's rubbish.

grep -n "loom" `grep -l "loom" tt4.txt` | grep -v "gloom"

               #this part gets the filenames with "loom"
#this part gets the lines with "loom"
                                          #this part gets the linenumber,
                                          #filename and actual line

Just use awk, it's much simpler than grep in letting you clearly express compound conditions.

If you want to skip lines that contains both loom and gloom:

awk '/loom/ && !/gloom/{ print FILENAME, FNR, $0 }' ~/projects/**/trunk/src/**/*.@(h|cpp)

or if you want to print them:

awk '/(^|[^g])loom/{ print FILENAME, FNR, $0 }' ~/projects/**/trunk/src/**/*.@(h|cpp)

and if the reality is you just want lines where loom appears as a word by itself:

awk '/\<loom\>/{ print FILENAME, FNR, $0 }' ~/projects/**/trunk/src/**/*.@(h|cpp)

Another solution without chaining grep:

egrep '(^|[^g])loom' ~/projects/**/trunk/src/**/*.@(h|cpp)

Between brackets, you exclude the character g before any occurrence of loom, unless loom is the first chars of the line.


-v is the "inverted match" flag, so piping is a very good way:

grep "loom" ~/projects/**/trunk/src/**/*.@(h|cpp)| grep -v "gloom"


Simply use! grep -v multiple times.

Content of file

[root@server]# cat file
1
2
3
4
5

Exclude the line or match

[root@server]# cat file |grep -v 3
1
2
4
5

Exclude the line or match multiple

[root@server]# cat file |grep -v 3 |grep -v 5
1
2
4

Question: search for 'loom' excluding 'gloom'.
Answer:

grep -w 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp)

A bit old, but oh well...

The most up-voted solution from @houbysoft will not work as that will exclude any line with "gloom" in it, even if it has "loom". According to OP's expectations, we need to include lines with "loom", even if they also have "gloom" in them. This line needs to be in the output "Arty is slooming in a gloomy day.", but this will be excluded by a chained grep like

grep -n 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp) | grep -v 'gloom'

Instead, the egrep regex example of Bentoy13 works better

egrep '(^|[^g])loom' ~/projects/**/trunk/src/**/*.@(h|cpp)

as it will include any line with "loom" in it, regardless of whether or not it has "gloom". On the other hand, if it only has gloom, it will not include it, which is precisely the behaviour OP wants.


How about just chaining the greps?

grep -n 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp) | grep -v 'gloom'

grep -n 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp) | grep -v 'gloom'

You can use grep -P (perl regex) supported negative lookbehind:

grep -P '(?<!g)loom\b' ~/projects/**/trunk/src/**/*.@(h|cpp)

I added \b for word boundaries.