The following command works as expected interactively, in a terminal.
$ find . -name '*.foo' -o -name '*.bar'
./a.foo
./b.bar
$
However, if I do this, I get no results!
$ ftypes="-name '*.foo' -o -name '*.bar'"
$ echo $ftypes
-name '*.foo' -o -name '*.bar'
$ find . $ftypes
$
My understanding was/is that $ftypes would get expanded by bash before find got a chance to run. In which case, the ftypes approach should also have worked.
What is going on here?
Many thanks in advance.
PS: I have a need to dynamically build a list of file types (the ftypes variable above) to be given to find later in a script.
Both answers so far have recommended using
eval, but that has a well-deserved reputation for causing bugs. Here’s an example of the sort of bizarre behavior you can get with this:Why didn’t it find the file ./a.foo? It’s because of exactly how that
evalcommand got parsed. bash’s parsing goes something like this (with some irrelevant steps left out):'*.foo'and'*.bar'— note that it hasn’t parsed the quotes, so it just treats them as part of the filename to match — and finds'wibble.foo'and substitutes it for'*.foo'). After this the command is roughlyeval find . -name "'wibble.foo'" -o "'*.bar'". BTW, if it had found multiple matches things would’ve gotten even sillier by the end.eval, and runs the whole parsing process over on the rest of the line.find, passing it the arguments “.”, “-name”, “wibble.foo”, “-o”, “-name”, and “*.bar”.findfinds one match for “*.bar”, but no match for “wibble.foo”. It never even knows you wanted it to look for “*.foo”.So what can you do about this? Well, in this particular case adding strategic double-quotes (
eval "find . $ftypes") would prevent the spurious wildcard substitution, but in general it’s best to avoidevalentirely. When you need to build commands, an array is a much better way to go (see BashFAQ #050 for more discussion):Note that you can also build the options bit by bit: