How to grep, excluding some patterns?

BashGrep

Bash Problem Overview


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

Bash Solutions


Solution 1 - Bash

How about just chaining the greps?

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

Solution 2 - Bash

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.

Solution 3 - Bash

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.

Solution 4 - Bash

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)

Solution 5 - Bash

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

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

Solution 6 - Bash

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

Solution 7 - Bash

/*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

Solution 8 - Bash

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.

Solution 9 - Bash

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

Solution 10 - Bash

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

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionLoomView Question on Stackoverflow
Solution 1 - BashhoubysoftView Answer on Stackoverflow
Solution 2 - BashBentoy13View Answer on Stackoverflow
Solution 3 - BashSamView Answer on Stackoverflow
Solution 4 - BashEd MortonView Answer on Stackoverflow
Solution 5 - BashOleg KokorinView Answer on Stackoverflow
Solution 6 - BashTiborcz KissView Answer on Stackoverflow
Solution 7 - BashJutoView Answer on Stackoverflow
Solution 8 - BashanubhavaView Answer on Stackoverflow
Solution 9 - BashJiminionView Answer on Stackoverflow
Solution 10 - BashAbhinandanView Answer on Stackoverflow