0

Why does find . -name "*.jav" work while find . -name *.jav does not work if there are more than 1 .jav files? What do the " " do and why is the "*.jav" not interpreted as a literal string?

1 Answers1

2

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 *.jav, first the shell expands *.jav to the list of matching files, e.g. file1.jav file2.jav file3.jav, and then the shell calls ls with the resulting list of arguments file1.jav file2.jav file3.jav. That's why you see the same list of file names with echo *.jav, even though echo knows nothing about files and doesn't care whether its arguments are file names.

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

  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, *.jav 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, *.jav.
  3. find looks for files whose name matches the pattern *.jav in any directory under the current directory.

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

  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, *.jav where the * is not quoted.
  2. Since the word *.jav 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, *.jav.
  4. find looks for files whose name matches the pattern *.jav in any directory under the current directory.

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

  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, *.jav where the * is not quoted.
  2. Since the word *.jav contains an unquoted wildcard character, the shell performs filename generation: *.jav 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.jav, file2.jav, file3.jav.
  4. find complains about a syntax error when it reaches file2.jav.

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

  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, *.jav where the * is not quoted.
  2. Since the word *.jav contains an unquoted wildcard character, the shell performs filename generation: *.jav 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.jav.
  4. find sees a perfectly valid command, but it is probably not what you intended: find is told to look for files called file.jav in any directory, not to look for any file matching *.jav.

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