Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

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

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

or

find . | grep ".*abc.*"
share|improve this question
    
find . -type f -iname "*abc*" would work... –  Sean C. Sep 12 '12 at 19:38
add comment

3 Answers 3

up vote 3 down vote accepted

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.

share|improve this answer
add comment

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.

share|improve this answer
add comment

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).

share|improve this answer
    
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. –  sasha.sochka Jan 26 at 15:16
    
@sasha.sochka find generates all the data and passes it through the pipe. It does this in parallel with grep. –  Gilles Jan 26 at 15:59
    
Oh, yeah, sorry the first time I missed the point . –  sasha.sochka Jan 26 at 16:22
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.