How do I find out the files in the current directory which do not contain the word foo
(using grep
)?
18 Answers
If your grep has the -L
(or --files-without-match
) option:
$ grep -L "foo" *

- 4,212
- 1
- 38
- 34

- 327,991
- 56
- 259
- 343
-
1As pointed out elsewhere ack helps avoid .svn (subversion) files by default. – GuruM Jan 10 '13 at 13:30
-
15@GuruM This can be done in GNU grep by exporting the variable `GREP_OPTIONS='--exclude-dir=.svn --exclude-dir=.git'` :^) – bufh Jul 11 '14 at 12:45
-
9Or the equivalent using [ag](https://github.com/ggreer/the_silver_searcher): `ag -L 'foo'` – bishop Jun 23 '15 at 19:48
-
1What if you want to find files without multiple things in the name. grep -L "foo,bar,baz" * ? – fIwJlxSzApHEZIl Sep 30 '16 at 16:06
-
12Works like magic! Hint: use `-rL` instead of `-L` to match subdirectories – Ufos Oct 18 '16 at 14:21
-
@anon58192932 you can use a regex like grep -e "foo|bar|baz" – rogerdpack Feb 02 '18 at 18:46
-
1General note for shell usage. The command is safer like this: `grep -L 'foo' ./*` In this case, results of pathname expansion (globbing) will never be mistaken for flags. Also, it makes it easier to split the result if it is stored in a variable or used through command substitution, since filenames are less likely to contain "./" than whitespace or dashes. – Larry Jun 20 '18 at 07:47
-
1@bufh While the options are correct newer grep versions output "grep: warning: GREP_OPTIONS is deprecated; please use an alias or script". – Simon Sobisch Aug 28 '18 at 06:13
-
1@Larry — A cleaner way to avoid globbing problems is to use the "empty" long option like this: `grep -L 'foo' -- *` The standard is that commands which take long options use `--` to indicate that there are no more options after this point. – Paddy Landau Jul 28 '19 at 13:50
-
@Paddy Landau: Yes, the option list terminator is especially better with grep. ./* can be used with older implementations of UNIX tools such as echo, which don't support the option list terminator. I just checked and luckily, each filename expands to a single word when globbing, so filenames that have whitespace in the middle, followed by a flag in their name do not confuse most programs. – Larry Aug 06 '19 at 13:26
-
On MacOS the option I wanted was `-v` or `--invert-match` which says "Selected lines are those not matching any of the specified patterns." I was using this as part of a `find . | grep "..."` command and it worked perfectly. – Topher Fangio Feb 28 '20 at 00:51
-
It works like a charm even on windows using [grep for windows](http://gnuwin32.sourceforge.net/packages/grep.htm). – Fil Oct 01 '21 at 17:07
-
This should be the accepted answer. – dr_ Aug 08 '23 at 12:56
You can do it with grep alone (without find).
grep -riL "foo" .
This is the explanation of the parameters used on grep
-L, --files-without-match
each file processed.
-R, -r, --recursive
Recursively search subdirectories listed.
-i, --ignore-case
Perform case insensitive matching.
If you use l
(lowercased) you will get the opposite (files with matches)
-l, --files-with-matches
Only the names of files containing selected lines are written

- 9,102
- 4
- 40
- 35
Take a look at ack
. It does the .svn
exclusion for you automatically, gives you Perl regular expressions, and is a simple download of a single Perl program.
The equivalent of what you're looking for should be, in ack
:
ack -L foo

- 15,395
- 32
- 113
- 196

- 91,102
- 13
- 100
- 152
The following command gives me all the files that do not contain the pattern foo
:
find . -not -ipath '.*svn*' -exec grep -H -E -o -c "foo" {} \; | grep 0

- 15,395
- 32
- 113
- 196

- 9,695
- 8
- 36
- 45
-
6You want to change the grep 0 at the end to grep 0$ (otherwise you get erroneous matches on files that have the character 0 in their filename). – clouseau Dec 23 '09 at 17:48
-
11@clouseau is mostly right... However, `grep '0$'` would match files with multiples of 10 lines too! You need `grep ':0$'` at the end to check for an explicit ':0' at the end of the line. Then you will only get files with zero lines matched. – TrinitronX Aug 10 '11 at 20:10
-
The UNIX I'm on didn't have versions of find or grep with these options, so I had to go with the "ack" command suggested in other comments. – KC Baltz Sep 08 '11 at 21:32
-
Highly inneficcient, as find will have to look for all the files, including those containing "foo", to then discard them on a second process. Will not work well for a big number of files being sought. A solution using an inbuilt find argument should be offered instead. – Daniel J. Jul 31 '20 at 10:15
The following command excludes the need for the find to filter out the svn
folders by using a second grep
.
grep -rL "foo" ./* | grep -v "\.svn"

- 15,395
- 32
- 113
- 196

- 1,013
- 10
- 15
You will actually need:
find . -not -ipath '.*svn*' -exec grep -H -E -o -c "foo" {} \; | grep :0\$

- 15,395
- 32
- 113
- 196

- 101
- 1
- 2
For completeness the ripgrep version:
rg --files-without-match "pattern"
You can combine with file type and search path, e.g.
rg --files-without-match -t ruby "frozen_string_literal: true" app/

- 17,364
- 6
- 77
- 85
Problem
I need to refactor a large project which uses .phtml
files to write out HTML using inline PHP code. I want to use Mustache templates instead. I want to find any .phtml
giles which do not contain the string new Mustache
as these still need to be rewritten.
Solution
find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$ | sed 's/..$//'
Explanation
Before the pipes:
Find
find .
Find files recursively, starting in this directory
-iname '*.phtml'
Filename must contain .phtml
(the i
makes it case-insensitive)
-exec 'grep -H -E -o -c 'new Mustache' {}'
Run the grep
command on each of the matched paths
Grep
-H
Always print filename headers with output lines.
-E
Interpret pattern as an extended regular expression (i.e. force grep
to behave as egrep).
-o
Prints only the matching part of the lines.
-c
Only a count of selected lines is written to standard output.
This will give me a list of all file paths ending in .phtml
, with a count of the number of times the string new Mustache
occurs in each of them.
$> find . -iname '*.phtml$' -exec 'grep -H -E -o -c 'new Mustache' {}'\;
./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml:0
./app/MyApp/Customer/View/Account/studio.phtml:0
./app/MyApp/Customer/View/Account/orders.phtml:1
./app/MyApp/Customer/View/Account/banking.phtml:1
./app/MyApp/Customer/View/Account/applycomplete.phtml:1
./app/MyApp/Customer/View/Account/catalogue.phtml:1
./app/MyApp/Customer/View/Account/classadd.phtml:0
./app/MyApp/Customer/View/Account/orders-trade.phtml:0
The first pipe grep :0$
filters this list to only include lines ending in :0
:
$> find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$
./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml:0
./app/MyApp/Customer/View/Account/studio.phtml:0
./app/MyApp/Customer/View/Account/classadd.phtml:0
./app/MyApp/Customer/View/Account/orders-trade.phtml:0
The second pipe sed 's/..$//'
strips off the final two characters of each line, leaving just the file paths.
$> find . -iname '*.phtml' -exec grep -H -E -o -c 'new Mustache' {} \; | grep :0$ | sed 's/..$//'
./app/MyApp/Customer/View/Account/quickcodemanagestore.phtml
./app/MyApp/Customer/View/Account/studio.phtml
./app/MyApp/Customer/View/Account/classadd.phtml
./app/MyApp/Customer/View/Account/orders-trade.phtml

- 1,431
- 1
- 14
- 18
When you use find, you have two basic options: filter results out after find has completed searching or use some built in option that will prevent find from considering those files and dirs matching some given pattern.
If you use the former approach on a high number of files and dirs. You will be using a lot of CPU and RAM just to pass the result on to a second process which will in turn filter out results by using a lot of resources as well.
If you use the -not keyword which is a find argument, you will be preventing any path matching the string on the -name or -regex argument behind from being considered, which will be much more efficient.
find . -not -regex ".*/foo/.*" -regex ".*"
Then, any path that is not filtered out by -not will be captured by the subsequent -regex arguments.

- 308
- 2
- 12
-
This searches for file _names_ not containing a certain pattern, right? I think OP meant file _contents_ not containing a certain pattern – codeling Jan 08 '21 at 09:43
-
Well, the question itself is a bit vague, I just interpreted it this way. Some people will for sure find this thread when looking to exclude file names. – Daniel J. Feb 23 '21 at 12:22
My grep does not have any -L option. I do find workaround to achieve this.
The ideas are :
- to dump all the file name containing the deserved string to a txt1.txt.
- dump all the file name in the directory to a txt2.txt.
make the difference between the 2 dump file with diff command.
grep 'foo' *.log | cut -c1-14 | uniq > txt1.txt grep * *.log | cut -c1-14 | uniq > txt2.txt diff txt1.txt txt2.txt | grep ">"

- 11
- 1
-
I forget the commands but instead of dumping file names, you can actually do a `diff` between two output streams (I think you surround the commands with parentheses, and there's an angle bracket in there somewhere too), if your systems supports it, which I guess is the question, since it doesn't support `grep -L` – Dexygen Nov 02 '17 at 16:36
find *20161109* -mtime -2|grep -vwE "(TRIGGER)"
You can specify the filter under "find" and the exclusion string under "grep -vwE". Use mtime under find if you need to filter on modified time too.

- 19
- 1
-
This seems to show me all lines without the string, the OP asks for just the file names. – Ben Farmer Mar 31 '17 at 07:26
Open bug report
As commented by @tukan, there is an open bug report for Ag regarding the -L
/--files-without-matches
flag:
As there is little progress to the bug report, the -L
option mentioned below should not be relied on, not as long as the bug has not been resolved. Use different approaches presented in this thread instead. Citing a comment for the bug report [emphasis mine]:
Any updates on this?
-L
completely ignores matches on the first line of the file. Seems like if this isn't going to be fixed soon, the flag should be removed entirely, as it effectively does not work as advertised at all.
The Silver Searcher - Ag (intended function - see bug report)
As a powerful alternative to grep
, you could use the The Silver Searcher - Ag:
A code searching tool similar to ack, with a focus on speed.
Looking at man ag
, we find the -L
or --files-without-matches
option:
... OPTIONS ... -L --files-without-matches Only print the names of files that don´t contain matches.
I.e., to recursively search for files that do not match foo
, from current directory:
ag -L foo
To only search current directory for files that do not match foo
, simply specify --depth=0
for the recursion:
ag -L foo --depth 0

- 70,367
- 12
- 127
- 192
-
This failes time to time due to the `-L` bug - https://github.com/ggreer/the_silver_searcher/issues/238 – tukan Sep 19 '18 at 13:21
-
@tukan thanks for the prompt. I have updated the answer; choosing not to delete the answer but instead opening with the info regarding the bug. – dfrib Sep 19 '18 at 13:29
another alternative when grep doesn't have the -L option (IBM AIX for example), with nothing but grep and the shell :
for file in * ; do grep -q 'my_pattern' $file || echo $file ; done

- 79
- 1
- 2
This may help others. I have mix of files Go
and with test
files. But I only need .go
files. So I used
ls *.go | grep -v "_test.go"
-v, --invert-match select non-matching lines see https://stackoverflow.com/a/3548465
Also one can use this with vscode to open all the files from terminal
code $(ls *.go | grep -v "_test.go")

- 5,026
- 3
- 31
- 58
-
2
-
@MathieuLongtin yes and I clearly stated that `I have mix of files Go and with test files. But I only need .go files. So I used` I just added this to help others that I use. – ARIF MAHMUD RANA Apr 30 '22 at 10:01
-
2
grep -irnw "filepath" -ve "pattern"
or
grep -ve "pattern" < file
above command will give us the result as -v finds the inverse of the pattern being searched
-
1This prints the lines which do not contain the pattern. You can add the `-l` option to print just the file name; but this still prints the names of any file which contains *any* line which does not contain the pattern. I believe the OP wants to find the files which do not contain any line which contains the pattern. – tripleee Jan 12 '16 at 06:51
-
Command you provided lists files in "filepath" with all their lines which doesn't contain "pattern". – aprodan May 30 '18 at 19:32
The following command could help you to filter the lines which include the substring "foo".
cat file | grep -v "foo"
-
2This prints lines which do not match, not names of files which do not contain a match on any line. To add insult to injury, it as a [useless use of `cat`](http://www.iki.fi/era/unix/award.html). – tripleee Jan 12 '16 at 06:53