Shell – Understanding Double Quoting

findquotingshellwildcards

I learned when I use command, double quoting treat all things as character except $, `, \ .

But, when use command like find -type f -name "*.jpg" *.jpg is inside double quotes. Then, it means we want to treat * and . as just a character. So, the find command should output regular file which has name *.jpg as it says, not pathname expansion implemented.

If we want to do pathname expansion, I think I have to do type command find -type f -name *.jpg(without double quoting).

But, the result is same. Why use double quoting in this command?

Best Answer

There is a subtlety to how wildcard expansion works. Change to a directory which contains no .jpg files and type

echo *.jpg

and it will output

*.jpg

In particular, the string *.jpg is left unmodified. If, however, you change to a directory containing .jpg files, for example suppose we have two files: image1.jpg and image2.jpg, then the echo *.jpg command will not output

image1.jpg image2.jpg

and the *.jpg gets expanded.

If you type

find . -name *.jpg

and there are no .jpg files in the directory you are when you type this, then find will receive the arguments ".", "-name" and "*.jpg". If, however, you type this command in a directory containing .jpg files, say image1.jpg and image2.jpg, then find will receive the arguments ".", "-name", "image1.jpg" and "image2.jpg", so will in effect run the command

find . -name image1.jpg image2.jpg

and find will complain. What can be really confusing if you omit the quotes is if there is a single .jpg file (say image1.jpg). Then the wildcard expansion will result in

find . -name image1.jpg

and the find command will find all files whose basename is image1.jpg.

Aside: This does lead to a useful bash idiom for seeing if any files match a given pattern:

if [ "$(echo *.jpg)" = "*.jpg" ]; then
    # *.jpg has no matches
else
    # *.jpg has matches
fi

though be warned that this will not work if there is a file called '*.jpg' in the current directory. To be more watertight, you can do

if [ "$(echo *.jpg)" = "*.jpg" ] && [ ! -e "*.jpg" ]; then
    # *.jpg has no matches
else
    # *.jpg has matches
fi

(While not directly relevant to the the question, I added this since it illustrates some of the aspects of how wildcard expansion works.)

Related Question