4

Of these two ways of searching a file recursively in all the subdirectories, which is faster / better ?

find . -regex ".*/.*abc.*"

or

find . | grep ".*abc.*"
1
  • 1
    find . -type f -iname "*abc*" would work...
    – Sean C.
    Commented Sep 12, 2012 at 19:38

3 Answers 3

6

UNIX file name can generally consist of octets (8-bit bytes), except for 0x00 (NULL) and 0x2F (/). Every other octet is valid. This includes such nice things as 0x0A (newline).

Your find example will handle file names with weird characters such as newline correctly.

Your find | grep example will give odd and incorrect results when faced with such a thing (it'll see one file called "line 1\nline 2" as two files).

You can use find -print0 | grep -z (if you're using GNU versions, e.g., on Linux); that'll preserve correctness. It'll use a little more memory. Note that you can tell find to use extended regular expressions (for example) using the -regextype option.

If you want to do some really complicated matching, you may like the find2perl script, which will convert a find command line in to a short perl program you can then edit to add in the complexity.

6

find . -regex ".*/.*abc.*" is faster because find . | grep ".*abc.*" has to have find generate all the data and pass it down to grep. The difference is likely to be small though. find . -regex ".*/.*abc.*" is also more reliable, because it works even in the rare case where you have file names with spaces.

Note that both commands look for files whose full path contains abc. This includes not only files whose name contains abc but also files contained in a directory whose name contains abc, recursively. To find only files whose name contains abc, use

find -name '*abc*'

In ksh, bash or zsh, you can run echo **/*abc* instead: **/ looks inside all subdirectories recursively. In ksh, you'll need to run set -o globstar first (put this in your ~/.kshrc). In bash, you'll need to run shopt -s globstar first (put this in your ~/.bashrc).

3
  • Why "find has to have find generate all the data"? As far as I know the data is automatically flushed regularly and passed to the pipe. That's how pipe usually works. Another example: try yes | cat. If cat waited for all the data from yes which is an infinite stream then it would never finally start writing data to the output. Commented Jan 26, 2014 at 15:16
  • 1
    @sasha.sochka find generates all the data and passes it through the pipe. It does this in parallel with grep. Commented Jan 26, 2014 at 15:59
  • Oh, yeah, sorry the first time I missed the point . Commented Jan 26, 2014 at 16:22
4

If you use pattern matching with find, you can add other predicates or behaviours:

# look only for matching directories
find . -regex ".*/.*abc.*" -type d

# run a command on each match
find . -regex ".*/.*abc.*" -exec echo 'I found a file named {}' ';'

find might be faster for just searching, too, because you don't need to spawn a grep process or perform any piping; but I doubt you would be able to notice.

You must log in to answer this question.

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