10

Imagine a source tree. There are xml files everywhere.

But since there is a XYZ.xml at the root of this tree it won't find my xml files.

find -iname *.xml

returns

./XYZ.xml

instead of

./XYZ.xml
./a/b/c/bob.xml
./b/d/top.xml
ilkkachu
  • 138,973
  • 1
    From man find: Please note that you should quote patterns as a matter of course, otherwise the shell will expand any wildcard characters in them. – artdanil Jun 29 '12 at 17:50

5 Answers5

19
find -iname '*.xml'

Otherwise, your shell expands *.xml to XYZ.xml, and the command that actually gets executed is

find -iname XYZ.xml

The reason it works if there are no XML files in the current directory is that shells generally leave wildcards unexpanded if they don't match anything. In general, any time you want wildcards to be expanded by a program other than the shell (e.g. by find, tar, scp, etc.) you need to quote them so the shell won't try to expand them itself.

cjm
  • 27,160
  • 1
    Thx, that's so simple, but I've been wondering how to get around that for months. I found that really weird, and to be a very inconsistent behavior, but now I understand since it's not find, but bash's fault. – Olivier Toupin May 29 '11 at 01:47
  • 3
    It's not bash's "fault" per-se but yours for not quoting wildcards that you wanted to pass as arguments. This goes for all programs that accept shell input. The shell expands them as globs unless they are quoted or escaped. – Caleb May 29 '11 at 05:59
  • 1
    I guess Olivier meant it in the meaning, that it is an issue of bash, not of find. – user unknown May 29 '11 at 14:03
6

You need to quote your argument like this:

find ./ -name '*.xml'

so that it gets passed to find instead of being expanded by the shell, then passed to find as the expanded version.

Caleb
  • 70,105
  • 1
    Ok, so if *.xml doesn't match anything in the current directory, it is passed on literally, which is why it works in the other case. Very helpful answer. – Eric Wilson Jun 09 '11 at 13:17
1

Wildcards are expanded by the shell, not by the command. find is one of the few commands that performs wildcard matching that's similar to the shell, in its own time.

When you run ls *.xml, first the shell expands *.xml to the list of matching files, e.g. file1.xml file2.xml file3.xml, and then the shell calls ls with the resulting list of arguments file1.xml file2.xml file3.xml. That's why you see the same list of file names with echo *.xml, even though echo knows nothing about files and doesn't care whether its arguments are file names.

When you run find . -name "*.xml":

  1. The shell parses the command line to recognize special characters and split it into words and puntuation. Here there's just a list of words find, ., -name, *.xml where the * is quoted. Since * is quoted, it's an ordinary character as far as the shell is concerned.
  2. The shell runs the command find with the specified list of arguments: ., -name, *.xml.
  3. find looks for files whose name matches the pattern *.xml in any directory under the current directory.

When you run find . -name *.xml and there are no files matching *.xml:

  1. The shell parses the command line to recognize special characters and split it into words and puntuation. Here there's just a list of words find, ., -name, *.xml where the * is not quoted.
  2. Since the word *.xml contains an unquoted wildcard character, the shell performs filename generation. Since there are no matching file names, the pattern remains unexpanded.
  3. The shell runs the command find with the resulting list of arguments, which is ., -name, *.xml.
  4. find looks for files whose name matches the pattern *.xml in any directory under the current directory.

When you run find . -name *.xml and the current directory contains file1.xml, file2.xml and file3.xml:

  1. The shell parses the command line to recognize special characters and split it into words and puntuation. Here there's just a list of words find, ., -name, *.xml where the * is not quoted.
  2. Since the word *.xml contains an unquoted wildcard character, the shell performs filename generation: *.xml is replaced by the list of matching file names.
  3. The shell runs the command find with the resulting list of arguments, which is ., -name, file1.xml, file2.xml, file3.xml.
  4. find complains about a syntax error when it reaches file2.xml.

When you run find . -name *.xml and the current directory contains a single matching file file.xml:

  1. The shell parses the command line to recognize special characters and split it into words and puntuation. Here there's just a list of words find, ., -name, *.xml where the * is not quoted.
  2. Since the word *.xml contains an unquoted wildcard character, the shell performs filename generation: *.xml is replaced by the list of matching file names.
  3. The shell runs the command find with the resulting list of arguments, which is ., -name, file.xml.
  4. find sees a perfectly valid command, but it is probably not what you intended: find is told to look for files called file.xml in any directory, not to look for any file matching *.xml.

(Shell evaluation and expansion has a lot of other features. I only mentioned the ones that are relevant here.)

(What I describe is the default behavior of most common shells: sh, bash, dash, ksh, … Some shells can be configured to display an error instead of running a command with unexpanded wildcards, or to expand non-matching wildcards to an empty list. None of those would help here.)

-1

This works on Solaris 10:

find /directory-to-search/* -prune -name "*gz"

jasonwryan
  • 73,126
-2

Please try:

find ./ -name *.xml
Tok
  • 10,744