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 Answers
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":
- 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,*.javwhere the*is quoted. Since*is quoted, it's an ordinary character as far as the shell is concerned. - The shell runs the command
findwith the specified list of arguments:.,-name,*.jav. findlooks for files whose name matches the pattern*.javin any directory under the current directory.
When you run find . -name *.jav and there are no files matching *.jav:
- 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,*.javwhere the*is not quoted. - Since the word
*.javcontains an unquoted wildcard character, the shell performs filename generation. Since there are no matching file names, the pattern remains unexpanded. - The shell runs the command
findwith the resulting list of arguments, which is.,-name,*.jav. findlooks for files whose name matches the pattern*.javin any directory under the current directory.
When you run find . -name *.jav and the current directory contains file1.jav, file2.jav and file3.jav:
- 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,*.javwhere the*is not quoted. - Since the word
*.javcontains an unquoted wildcard character, the shell performs filename generation:*.javis replaced by the list of matching file names. - The shell runs the command
findwith the resulting list of arguments, which is.,-name,file1.jav,file2.jav,file3.jav. findcomplains about a syntax error when it reachesfile2.jav.
When you run find . -name *.jav and the current directory contains a single matching file file.jav:
- 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,*.javwhere the*is not quoted. - Since the word
*.javcontains an unquoted wildcard character, the shell performs filename generation:*.javis replaced by the list of matching file names. - The shell runs the command
findwith the resulting list of arguments, which is.,-name,file.jav. findsees a perfectly valid command, but it is probably not what you intended:findis told to look for files calledfile.javin 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.)
- 829,060
-
You forgot to mention what OP asked for: Why does it work with exactly one matching filename - but I think this can easily be derived from your answer. – rexkogitans Oct 16 '19 at 06:44
-
@rexkogitans Good point, added. This case wasn't explicitly mentioned in the question but it's an important one. – Gilles 'SO- stop being evil' Oct 16 '19 at 06:50
-
well done, especially saying "but it is probably not what you intended" in point 4. – rexkogitans Oct 16 '19 at 08:25