1085

I want to traverse all subdirectories, except the node_modules directory.

Guy Avraham
  • 3,482
  • 3
  • 38
  • 50
TIMEX
  • 259,804
  • 351
  • 777
  • 1,080
  • 3
    See http://superuser.com/q/66715/59933 – borrible Jul 03 '11 at 20:55
  • 78
    If you are grepping for code in a git repository and `node_modules` is in your `.gitignore`, `git grep "STUFF"` is the easiest way. `git grep` searches the tracked files in the working tree, ignoring everything from `.gitignore` – 0xcaff Dec 22 '16 at 21:51
  • 7
    An example for node: `grep -R --exclude-dir={node_modules,bower_components} "MyString" | cut -c1-"$COLUMNS"` -- further you could always alias this in the shell to 'nodegrep' or whatever and use a command argument as string input.. – B. Shea Mar 21 '18 at 21:59

14 Answers14

1331

Recent versions of GNU Grep (>= 2.5.2) provide:

--exclude-dir=dir

which excludes directories matching the pattern dir from recursive directory searches.

So you can do:

grep -R --exclude-dir=node_modules 'some pattern' /path/to/search

For a bit more information regarding syntax and usage see

For older GNU Greps and POSIX Grep, use find as suggested in other answers.

Or just use ack (Edit: or The Silver Searcher) and be done with it!

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
  • 4
    @Manocho: If you think `ack` is great, try The Silver Searcher and see the speed increase! – johnsyweb Nov 13 '13 at 22:08
  • 47
    Syntax for the impatient: `--exclude-dir=dir` uses `grep`'s regular expression patterns, _not_ shell's file globbing. Patterns work on paths relative to your current directory. So use pattern `--exclude-dir=dir`, not `--exclude-dir="/root/dir/*"`. – tanius Feb 08 '14 at 17:39
  • Note if your grep doesn't support it, you can use `ggrep` – aug Aug 26 '15 at 22:09
  • 25
    If you wish to exclude multiple dirs from the search, is there a better option than to use : `$ grep -r --exclude-dir=dir1 --exclude-dir=dir2 "string" /path/to/search/dir` ? – Darshan Chaudhary Nov 25 '15 at 06:21
  • @DarshanChaudhary: What do you mean by 'better'? That looks like a good approach to me. – johnsyweb Nov 25 '15 at 08:50
  • 2
    @Johnsyweb, I was hoping for something where I did not have to mention `--exclude-dir` multiple times. – Darshan Chaudhary Nov 25 '15 at 09:53
  • Unfortunately the default grep comes with OS X 10.11 is still 2.5.1 – Tyler Liu Jan 06 '16 at 13:02
  • @TylerLong: I use `grep` from homebrew-dupes: https://github.com/Homebrew/homebrew-dupes/blob/master/grep.rb – johnsyweb May 06 '16 at 02:24
  • 19
    I probably spent way too much time on this than any sane person, but I can't for the life of me figure out how to exclude a subdirectory from the search - `grep -r --exclude-dir=public keyword .` works, but `grep -r --exclude-dir='public/dist' keyword .` does not. I tried adding regex wildcards, escaping characters etc, but nothing seems to help. – dkobozev Jul 06 '16 at 23:47
  • 109
    Exclude multiple directories like so: `grep -r "Request" . --exclude-dir={node_modules,git,build}` – maverick97 Jul 12 '16 at 08:00
  • Is there a way to add this to all grep commands I make so I don't need to manually type it every time? – Matthew Herbst May 30 '17 at 22:02
  • 2
    @MatthewHerbst: You could wrap `grep` in an alias or script. – johnsyweb May 31 '17 at 00:08
  • @MatthewHerbst you can make an alias as suggested or export it using GREP_OPTIONS (or a similar) variable I believe. I prefer the alias route in case I really do need to search in one of the excluded directories or with different options I can just bypass the alias. – dragon788 Jul 02 '17 at 05:44
  • 1
    @dragon788: `GREP_OPTIONS` is deprecated: https://git.savannah.gnu.org/cgit/grep.git/commit/?id=50d843674e5df9f5d0111f07bbff8ce07c19df6a – johnsyweb Jul 04 '17 at 06:41
  • 1
    @dkobozev to exclude specific subdirectories you must start with the dot, so using `grep -r --exclude-dir='./public/dist' keyword` should be what you're looking for – ErikAGriffin Mar 09 '19 at 19:43
  • 1
    Don't use the relative path, i.e. just `--exclude-dir=dir_name` instead of `--exclude-dir=folder/dir_name` which won't do the desired filtering. – Nagev May 30 '19 at 14:07
  • 1
    Would someone mind to explain this a little better? I need to exclude directories that are not subdirectories of the one I'm searching from. Say I write my `grep` command in `~/` and I need to exclude `/media` from the search. I don't think just writing `--exclude-dir=media` would do the trick, because it's not a subdirectory of `~/`. Am I wrong? Should I just `cd` into `/` (it seems odd to me)? – Andyc Apr 07 '21 at 20:43
  • 2
    @dkobozev I couldn't get it to work either. Had to use the following workaround instead: `grep -r keyword | grep -v 'public/dist'` – Krzysztof Wołowski Feb 23 '22 at 08:00
  • Don't add spaces after commas in `--exclude-dir={dir1,dir2}` – Levi Baguley Dec 15 '22 at 22:46
  • @MatthewHerbst I use `EXCLUDE_NODEMODULES="--exclude-dir=node_modules --exclude-dir=.git"` in my .bashrc. So I use the same command every time (grep), but I type fast `grep $EXC -r ...` which is completed by bash to `grep --exclude... -r ...` – Gibezynu Nu Jan 18 '23 at 15:35
612

SOLUTION 1 (combine find and grep)

The purpose of this solution is not to deal with grep performance but to show a portable solution : should also work with busybox or GNU version older than 2.5.

Use find, for excluding directories foo and bar :

find /dir \( -name foo -prune \) -o \( -name bar -prune \) -o -name "*.sh" -print

Then combine find and the non-recursive use of grep, as a portable solution :

find /dir \( -name node_modules -prune \) -o -name "*.sh" -exec grep --color -Hn "your text to find" {} 2>/dev/null \;

SOLUTION 2 (using the --exclude-dir option of grep):

You know this solution already, but I add it since it's the most recent and efficient solution. Note this is a less portable solution but more human-readable.

grep -R --exclude-dir=node_modules 'some pattern' /path/to/search

To exclude multiple directories, use --exclude-dir as:

--exclude-dir={node_modules,dir1,dir2,dir3}

SOLUTION 3 (Ag)

If you frequently search through code, Ag (The Silver Searcher) is a much faster alternative to grep, that's customized for searching code. For instance, it automatically ignores files and directories listed in .gitignore, so you don't have to keep passing the same cumbersome exclude options to grep or find.

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
hornetbzz
  • 9,188
  • 5
  • 36
  • 53
  • 3
    this combination searches faster than `--exclude-dir=dir` and it shows results with colors - easy to read – Maxim Yefremov Oct 08 '13 at 01:51
  • 32
    "this combination" `find ... -exec` is not faster than `grep --exclude-dir` for me. Huge advantage to grep (about five times faster with 26k+ files, filtered out of 38k+ on an HDD), unless you replace the `\;` with `+` for the find/exec combo. Then grep is "only" about 30% faster. The grep syntax is also human-readble :). – Kjell Andreassen Jan 27 '14 at 17:48
  • Agreed, since this is obvious. Some busyboxes does not have the GREP command. – hornetbzz Feb 16 '18 at 12:57
  • `grep --exclude-dir=dir -rin "text" .` works fine for me. `ag text .` (silver_searcher) works superb – user9869932 Oct 26 '18 at 11:10
  • 18
    also noting that you can exclude multiple with `--exclude-dir={dir1,dir2}` – suh Nov 07 '18 at 15:11
  • 10
    I'm not the least bit surprised that `node_modules` is the canonical example. – pdoherty926 Jan 16 '19 at 18:21
  • grep -nr would add line number of match to the results – Ewoks Jul 30 '20 at 09:46
  • Don't add spaces after commas in `--exclude-dir={dir1,dir2}` – Levi Baguley Dec 15 '22 at 22:44
121

If you want to exclude multiple directories:

"r" for recursive, "l" to print only names of files containing matches and "i" to ignore case distinctions :

grep -rli --exclude-dir={dir1,dir2,dir3} keyword /path/to/search

Example : I want to find files that contain the word 'hello'. I want to search in all my linux directories except proc directory, boot directory, sys directory and root directory :

grep -rli --exclude-dir={proc,boot,root,sys} hello /

Note : The example above needs to be root

Note 2 (according to @skplunkerin) : do not add spaces after the commas in {dir1,dir2,dir3}

Azodium
  • 1,785
  • 1
  • 13
  • 10
117

This syntax

--exclude-dir={dir1,dir2}

is expanded by the shell (e.g. Bash), not by grep, into this:

--exclude-dir=dir1 --exclude-dir=dir2

Quoting will prevent the shell from expanding it, so this won't work:

--exclude-dir='{dir1,dir2}'    <-- this won't work

The patterns used with --exclude-dir are the same kind of patterns described in the man page for the --exclude option:

--exclude=GLOB
    Skip files whose base name matches GLOB (using wildcard matching).
    A file-name glob can use *, ?, and [...]  as wildcards, and \ to
    quote a wildcard or backslash character literally.

The shell will generally try to expand such a pattern itself, so to avoid this, you should quote it:

--exclude-dir='dir?'

You can use the curly braces and quoted exclude patterns together like this:

--exclude-dir={'dir?','dir??'}
Derek Veit
  • 3,650
  • 2
  • 16
  • 15
28

If you are grepping for code in a git repository and node_modules is in your .gitignore, you can use git grep. git grep searches the tracked files in the working tree, ignoring everything from .gitignore

git grep "STUFF"
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
0xcaff
  • 13,085
  • 5
  • 47
  • 55
  • This is very useful tip.Thanks. – NKM Jun 17 '19 at 02:50
  • This is an incredible way to limit wading through tons of generated files (dist, bin, etc.) and it pipes into a utility like `less` automagically so you have a scrollable search result. Fantastic! – Blake Neal May 10 '23 at 18:28
17

Frequently use this:

grep can be used in conjunction with -r (recursive), i (ignore case) and -o (prints only matching part of lines). To exclude files use --exclude and to exclude directories use --exclude-dir.

Putting it together you end up with something like:

grep -rio --exclude={filenames comma separated} \
--exclude-dir={directory names comma separated} <search term> <location>

Describing it makes it sound far more complicated than it actually is. Easier to illustrate with a simple example.

Example:

Suppose I am searching for current project for all places where I explicitly set the string value debugger during a debugging session, and now wish to review / remove.

I write a script called findDebugger.sh and use grep to find all occurrences. However:

For file exclusions - I wish to ensure that .eslintrc is ignored (this actually has a linting rule about debugger so should be excluded). Likewise, I don't want my own script to be referenced in any results.

For directory exclusions - I wish to exclude node_modules as it contains lots of libraries that do reference debugger and I am not interested in those results. Also I just wish to omit .idea and .git hidden directories because I don't care about those search locations either, and wish to keep the search performant.

So here is the result - I create a script called findDebugger.sh with:

#!/usr/bin/env bash
grep -rio --exclude={.eslintrc,findDebugger.sh} \
--exclude-dir={node_modules,.idea,.git} debugger .
arcseldon
  • 35,523
  • 17
  • 121
  • 125
  • I believe the "r" option should be printed with an upper case "-R". – hornetbzz Mar 12 '18 at 18:01
  • 1
    Interesting. "r" has always worked for me on nix and mac. – arcseldon Mar 13 '18 at 02:34
  • When I wrote [my answer](https://stackoverflow.com/a/8692318/78845), I used `-R` (I don't recall why now). I typically use `-r`. It turns out that the uppercase version [follows symlinks](https://git.savannah.gnu.org/cgit/grep.git/tree/src/grep.c#n1968). TIL. – johnsyweb Apr 21 '20 at 06:56
  • @Johnsyweb - thanks. upvoted your answer - don't recall when, likely in 2016 when I added this one :) – arcseldon Apr 22 '20 at 13:39
15

Many correct answers have been given here, but I'm adding this one to emphasize one point which caused some rushed attempts to fail before: exclude-dir takes a pattern, not a path to a directory.

Say your search is:

grep -r myobject

And you notice that your output is cluttered with results from the src/other/objects-folder. This command will not give you the intended result:

grep -r myobject --exclude-dir=src/other/objects-folder

And you may wonder why exclude-dir isn't working! To actually exclude results from the objects-folder, simply do this:

grep -r myobject --exclude-dir=objects-folder

In other words, just use the folder name, not the path. Obvious once you know it.

From the man page:

--exclude-dir=GLOB
Skip any command-line directory with a name suffix that matches the pattern GLOB. When searching recursively, skip any subdirectory whose base name matches GLOB. Ignore any redundant trailing slashes in GLOB.

Nagev
  • 10,835
  • 4
  • 58
  • 69
  • 2
    Why on the planet didn't I scroll down to this answer before I posted my comment/question up above? I unfortunately have the bad habit to ignore answers with less upvotes, but this explains what I was doing wrong, so thanks Nagev. – Andyc Apr 11 '21 at 06:04
  • 1
    I would go crazy because I used the full path to a subfolder, but it did not work. After I saw this comment, I used the folder name ONLY and it finally worked! THANKS – babis21 Apr 25 '23 at 14:18
9

You could try something like grep -R search . | grep -v '^node_modules/.*'

DipSwitch
  • 5,470
  • 2
  • 20
  • 24
  • 42
    Not such a good solution in some cases. For example: If 'node_modules' directory is a huge one with lots of false positive matches (hence the need to filter out the directory) then the first grep is wasting a lot of time searching through a sub-directory and THEN the second grep filtering out the matches. It's faster to exclude node_modules in the first grep itself. – GuruM Dec 13 '12 at 08:22
  • 2
    i don't care about the slowness, I can look at the command and know what it does – dansch Apr 22 '14 at 18:39
  • 1
    Ditto for Guru's comment. A grep of `/var` hangs when it hits `/var/run` in my case. Hence the reason I want to avoid the directory in the first place. – jww Aug 31 '15 at 09:48
  • 5
    `--exclude-dir` is the best solution as of 2016. – Omar Tariq Dec 13 '16 at 22:00
4

Very useful, especially for those dealing with Node.js where we want to avoid searching inside "node_modules":

find ./ -not -path "*/node_modules/*" -name "*.js" | xargs grep keyword
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Nestor Urquiza
  • 2,821
  • 28
  • 21
2

A simple working command:

root/dspace# grep -r --exclude-dir={log,assetstore} "creativecommons.org"

Above I grep for text "creativecommons.org" in current directory "dspace" and exclude dirs {log,assetstore}.

Done.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Dung
  • 19,199
  • 9
  • 59
  • 54
1

Step 1:

vim ~/.bash_profile

search() {
    grep -InH -r --exclude-dir=*build* -e "$1" .
}

Step 2:

source ~/.bash_profile

Usage:

search "<string_to_be_searched>"

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Francis Bacon
  • 4,080
  • 1
  • 37
  • 48
1
find . ! -name "node_modules" -type d 
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Jack
  • 19
  • 1
0

This one works for me:

grep <stuff> -R --exclude-dir=<your_dir>
vvvvv
  • 25,404
  • 19
  • 49
  • 81
angelo.mastro
  • 1,680
  • 17
  • 14
-5

A simpler way would be to filter your results using "grep -v".

grep -i needle -R * | grep -v node_modules

Morris
  • 169
  • 8
  • 15
    This is effectively the same answer DipSwitch provided 3 years earlier. It has the same problems, too. – jww Aug 31 '15 at 09:53