787

I'd like to find the files in the current directory that contain the text "chrome".

$ find . -exec grep chrome
find: missing argument to `-exec'

What am I doing wrong?

3

6 Answers 6

1189

You missed a ; (escaped here as \; to prevent the shell from interpreting it) or a + and a {}:

find . -exec grep chrome {} \;

or

find . -exec grep chrome {} +

find will execute grep and will substitute {} with the filename(s) found. The difference between ; and + is that with ; a single grep command for each file is executed whereas with + as many files as possible are given as parameters to grep at once.

13
  • 42
    If you use the \; ending construct grep is passed one file at a time, so it doesn't display the file name by default, only the matched lines. To get a file list instead add use grep -ls inside of the find construct.
    – Caleb
    Commented May 10, 2011 at 6:55
  • 16
    find . -exec grep foo {} + will show you output like this ./dir/file.py:from foo import bar
    – s g
    Commented Apr 17, 2015 at 20:30
  • 14
    find . -exec grep foo {} \; will show you output like this from foo import bar
    – s g
    Commented Apr 17, 2015 at 20:31
  • 12
    find . -exec grep -l foo {} + will show you output like this ./dir/file.py
    – s g
    Commented Apr 17, 2015 at 20:32
  • 11
    find . -exec grep -l foo {} \; will show you output like this ./dir/file.py
    – s g
    Commented Apr 17, 2015 at 20:33
68

You don't need to use find for this at all; grep is able to handle opening the files either from a glob list of everything in the current directory:

grep chrome *

...or even recursively for folder and everything under it:

grep chrome . -R
8
  • 31
    grep will choke if the expansion goes over ARG_MAX. -R will visit everything while using find one can more easily add primitives to exclude certain files (-name, etc) or not even visit subtrees (-prune).
    – Mel
    Commented May 9, 2011 at 23:58
  • 9
    Good points @Mel. My point was that in all likelihood the asking party was making things more complex than they needed to be by introducing find when grep could do the job, but in some cases it would be more effective to to use find to fine tine the file list before going out to grep.
    – Caleb
    Commented May 10, 2011 at 6:53
  • 4
    @Mel grep does not choke in such a case, exec does.
    – Chris Down
    Commented Mar 26, 2013 at 6:21
  • 3
    @Yaba My answer states how to handle the case of files spread over subdirectories.
    – Caleb
    Commented Sep 14, 2018 at 12:56
  • 3
    @DaCheetah This is a misunderstanding -- the above comment has nothing to do with find -exec. exec here refers to libc functions which directly call the execve syscall (eg. exec{ve,vp,lp,...}), not find -exec which does its own manipulation prior. The point is that grep never even sees the arguments to choke on them, it's the kernel that rejects it.
    – Chris Down
    Commented May 4, 2020 at 15:31
24
find . | xargs grep 'chrome'

you can also do:

find . | xargs grep 'chrome' -ls

The first shows you the lines in the files, the second just lists the files.

Caleb's option is neater, fewer keystrokes.

6
  • 15
    The problem with xargs is that it expects its input to be quoted in a peculiar way that find doesn't produce. So find … | xargs … doesn't work if you have file names containing whitespace or \'". Commented May 10, 2011 at 6:31
  • 4
    @Gilles You can get around that problem by using something like find . | xargs -n1 -iX grep "X" 'chrome' so that arguments are fed one at a time and quoted. Obviously this is a horribly inefficient way to handle this example, but for some situations it's nice.
    – Caleb
    Commented May 10, 2011 at 11:56
  • 1
    For completeness we should also mention the -i option for case insensitivity with 'grep'. Also there is -iname in find for case insensitivity.
    – Mathew
    Commented May 10, 2011 at 12:01
  • 15
    @Caleb: The only 100% reliable way to have xargs cope with Linux filenames is find ... -print0 | xargs -0, using NUL as separator. Alternative - xargs -d '\n' using newline as separator, 99% reliability. Commented May 10, 2011 at 20:18
  • I use this often, but it will fail for very long lists of filenames, at which point find -exec becomes the winner.
    – Spacemoose
    Commented Jul 5, 2015 at 9:08
11

Find is one way and you can try the_silver_searcher then all you need to do is

ag chrome

It will search chrome in all files (include sub directories) and it is faster than find

1
9

Here's an example of how I usually use find/exec...

find  . -name "*.py" -print -exec fgrep hello {} \;

This searches recursively for all .py files, and for each file print(s) out the filename and fgrep's for 'hello' on that (for each) file. Output looks like (just ran one today):

./r1.py
./cgi-bin/tst1.py
print "hello"
./app/__init__.py
./app/views.py
./app/flask1.py
./run.py
./tst2.py
print "hello again"
1
  • 1
    not to mess with your workflow, but you might like: find . -name "*.py" -exec fgrep -l hello {} \; -- it'll print the filenames of files that match, and nothing else
    – Jeff Schaller
    Commented Aug 23, 2017 at 16:19
5

To see list of files instead of lines:

grep -l "chrome" *

or:

grep -r -l "chrome" .

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .