The
find
command on Unix/Linux and Apple OS X systems allows
you to specify multiple criteria to be used for a search. For instance,
suppose I have a directory named man
and a file named
manual.txt
. If I wanted to find any files or directories
containing "man" within their names, I could issue the command below. If
the directory in which the find command was executed contained a subdirectory
named man
and a text file named manual.txt
, I
would see the results shown below:
$ find . -name \*man\* ./man ./manual.txt
Note: the backslashes before the asterisks are
"escape characters",
i.e., they tell the shell not to interpret the asterisk before the find
command sees it - see
What is the difference between \*.xml and *.xml in find command in Linux/mac.
Another alternative is to enclose the *man*
within double quotes.
$ find . -name "*man*" ./man ./manual.txt
But, if I only want to find items that have "man" in the name which are directories, I could use the following to specify I only want to see items where the file is of type directory ("d" represents directory and "f" represents a regular file):
$ find . -name \*man\* -type d ./man
By default, the find command will use a
logical and for the two
conditions, i.e., both conditions must be met. I could explicitly state
I want to "and" the two conditions with a -a
, but it isn't
necessary to do so in this case.
$ find . -name \*man\* -a -type d ./man
But what if I want to to specify a
logical "or", i.e.
that I want results returned where either of two conditions are met? E.g.,
suppose I want to find all files where the filename contains man
or guide
. Then I need to use a -o
parameter.
$ find . -name "*man*" -o -name "*guide*" ./man ./manual.txt ./guide.txt
Suppose I only wanted to see only files with man
or guide
in the filename that are "regular" files and not any
directories. I could use -type f
to specify that I only want
to see regular files.
$ find . -name "*man*" -o -name "*guide*" -type f ./man ./manual.txt ./guide.txt
As you can see, the directory man
is still returned. To get
the results I want, i.e., to not have the directory man
appear
in the results, I need to enclose the "or" condtions within parentheses.
$ find . \( -name "*man*" -o -name "*guide*" \) -type f ./manual.txt ./guide.txt
Note: you also need to
"escape" the meaning
of (
and )
by preceding them with the backslash
escape character.
Otherwise, you will get an "unexpected token" error message.
$ find . (-name "*man*" -o -name "*guide*") -type f -bash: syntax error near unexpected token `('
And you need to put a space after the left parenthesis and before the right parenthesis or you will receive an "invalid predicate" error message.
$ find . \(-name "*man*" -o -name "*guide*"\) -type f find: invalid predicate `(-name'
As another example, suppose I want to find all HTML or PHP files that
contain the word "Geek" within them when the HTML files have a .html
extension and the PHP files have a .php extension on the file names. Then
I need to use a -o
between the conditions to specify that
I want to see results if the file has an extension of .html or .php.
$ find . \( -name "*.php" -o -name "*.html" \) -exec grep -i "Geeks" {} /dev/null \; ./temp.php:1Geeks ./temp.html:2Geeks
Whenever a file has a name that ends in .html or .php, the file
contents are sent to the grep command for examination. To specify
that I want to use a logical or, the -o
is placed between
-name "*.php"
and -name "*.html"
. Again, I also
have to include the two conditions within parentheses to ensure that
the "or" condition is checked before sending the results to grep for
examination of the contents of the files. If the parentheses aren't used,
I would only see one of the files returned.
$ find . -name "*.php" -o -name "*.html" -exec grep -i "Geeks" {} /dev/null \; ./temp.html:2Geeks