Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to grep, excluding some patterns?

Tags:

grep

bash

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

like image 595
Loom Avatar asked Aug 27 '13 14:08

Loom


People also ask

How do I exclude multiple items in grep?

Specify Multiple Patterns. The -e flag allows us to specify multiple patterns through repeated use. We can exclude various patterns using the -v flag and repetition of the -e flag: $ grep -ivw -e 'the' -e 'every' /tmp/baeldung-grep Time for some thrillin' heroics.

How do you filter with grep?

grep is very often used as a "filter" with other commands. It allows you to filter out useless information from the output of commands. To use grep as a filter, you must pipe the output of the command through grep . The symbol for pipe is " | ".

How do you grep a case insensitive?

Case Insensitive Search By default, grep is case sensitive. This means that the uppercase and lowercase characters are treated as distinct. To ignore case when searching, invoke grep with the -i option (or --ignore-case ).


9 Answers

How about just chaining the greps?

grep -n 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp) | grep -v 'gloom'
like image 193
houbysoft Avatar answered Oct 16 '22 00:10

houbysoft


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.

like image 45
Bentoy13 Avatar answered Oct 15 '22 23:10

Bentoy13


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.

like image 38
Sam Avatar answered Oct 16 '22 00:10

Sam


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)
like image 38
Ed Morton Avatar answered Oct 15 '22 23:10

Ed Morton


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

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

like image 37
Oleg Kokorin Avatar answered Oct 15 '22 23:10

Oleg Kokorin


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\|5"
1
2
4
like image 25
Tiborcz Kiss Avatar answered Oct 15 '22 23:10

Tiborcz Kiss


/*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
like image 22
Juto Avatar answered Oct 16 '22 00:10

Juto


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.

like image 24
anubhava Avatar answered Oct 16 '22 00:10

anubhava


grep -n 'loom' ~/projects/**/trunk/src/**/*.@(h|cpp) | grep -v 'gloom'
like image 45
Jiminion Avatar answered Oct 16 '22 01:10

Jiminion