Bash – find and globbing (and wildcards)

bashfind

I tried finding some files (*.e*) that are in the same directory as another file (md.tpr). I needed to list them (for further processing) using the following:

find . -name md.tpr -execdir ls *.e* \;

I tried a few variants of this command and some others (including single quoting the command passed to -execdir or passing it as sh -c 'ls *.e*' or eval 'ls *.e*' to name a few). It appears that globbing is not working when passed to -exec or -execdir. The error I get when running the above command is:

ls: *.e*: No such file or directory

Just as a sanity check, I did -execdir pwd and it prints what it should, so it appears that it's a problem with globbing, as those *.e* files do exist in the directories listed with this test.

Now, I was able to solve this problem in a much less elegant way but it just baffled me why globbing and wildcards wouldn't work here. Any ideas? Or am I completely off the track?

I use bash 3.2.25 (old but I don't have admin rights on that system).

Also, interestingly, if I do

find ~ -name .bashrc -maxdepth 2 -execdir ls -d .b* \;

it doesn't work unless it's done from $HOME.

Best Answer

Both the find command and the shell are capable of file globbing.

This is unusual - most commands are NOT capable of globbing and rely entirely on the shell to expand globs. But find is a super duper mega power user tool that you can very easily hurt yourself with!

Example: when you do the command

   find /path -iname *.txt

The first thing that happens is the shell attempts to find all files that match *.txt in the current directory. If it finds any, it substitutes the names of all the matching files for the glob, and then calls the find command. The find command never sees the glob if this happens, the shell has expanded it out of existence.

But if there are no files in the current directory that match the glob, the shell shrugs its metaphorical shoulders and passes the glob unchanged to find. So at that point, the find command (which understands globs, remember) will output the names of all files it finds under /path that match the glob.

So using globs this way means that find will behave differently depending on the content of the current directory. This is almost certainly not what you want!

To prevent the shell from tampering with globs before find sees them, escape the globs with appropriate shell metacharacter quoting. Usually this just means putting your globstrings in single quotes like this

   find /path -iname '*.txt'

Remember, GLOBS ARE NOT REGEXES - the glob ".*" and the regex ".*" are very different and do not match the same strings!

Related Question