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')
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…