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:
$ touch a.foo b.bar "'wibble.foo'" $ ftypes="-name '*.foo' -o -name '*.bar'" $ eval find . $ftypes ./b.bar
Why didn't it find the file ./a.foo? It's because of exactly how that eval command got parsed. bash's parsing goes something like this (with some irrelevant steps left out):
- bash looks for quotes first (none found -- yet).
- bash substitutes variables (but doesn't go back and look for quotes in the substituted values -- this is what lead to the problem in the first place).
- bash does wildcard matching (in this case it looks for files matching
'*.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 roughly eval find . -name "'wibble.foo'" -o "'*.bar'". BTW, if it had found multiple matches things would've gotten even sillier by the end. - bash sees that the command on the line is
eval, and runs the whole parsing process over on the rest of the line. - bash does quote matching again, this time finding two single-quoted strings (so it'll skip most parsing on those parts of the command).
- bash looks for variables to substitute and wildcards to matching, etc, but there aren't any in the unquoted sections of the command.
- Finally, bash runs
find, passing it the arguments ".", "-name", "wibble.foo", "-o", "-name", and "*.bar". find finds 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 avoid eval entirely. When you need to build commands, an array is a much better way to go (see BashFAQ #050 for more discussion):
$ ftypes=(-name '*.foo' -o -name '*.bar') $ find . "${ftypes[@]}" ./a.foo ./b.bar
Note that you can also build the options bit by bit:
$ ftypes=(-name '*.foo') $ ftypes+=(-o -name '*.bar') $ ftypes+=(-o -name '*.baz')
"... -name '*.foo'remain in the interpolated value. If you wantftypesto be a variable (not necessarily a good idea anyway) try with just... -name *.foo- the double quotes preve*.foowould result in expanded list of files which will be passed tofind. I anyway tried what you said, and got this error:find: paths must precede expression Usage: find [-H] [-L] [-P] [path...] [expression]touch fnord.foo ick.bar none.svch; ftypes="-name *.foo -o -name *.bar"; find . $ftypesdoes exactly what I expect.touch fnord.foo another.foo ick.bar none.svch; ftypes="-name *.foo -o -name *.bar"; find . $ftypes.evalis the best option then.