/usr/bin/find:无法动态构建其参数
以下命令在终端中按预期交互运行。
$ find . -name '*.foo' -o -name '*.bar'
./a.foo
./b.bar
$
但是,如果我这样做,我不会得到任何结果!
$ ftypes="-name '*.foo' -o -name '*.bar'"
$ echo $ftypes
-name '*.foo' -o -name '*.bar'
$ find . $ftypes
$
我的理解是,在 find
有机会运行之前,$ftypes
会被 bash
扩展。在这种情况下,ftypes
方法也应该有效。
这是怎么回事?
非常感谢。
PS:我需要动态构建文件类型列表(上面的 ftypes 变量),以便稍后在脚本中查找。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
到目前为止,两个答案都建议使用
eval
,但它因导致错误而享有盛誉。以下是您可能会遇到的奇怪行为的示例:为什么它没有找到文件 ./a.foo?这是因为
eval
命令的解析方式。 bash 的解析是这样的(省略了一些不相关的步骤):'*.foo'
和'*.bar'
匹配的文件 - 请注意,它尚未解析引号,因此它只是将它们视为要匹配的文件名的一部分 - 并找到'wibble.foo'
并将其替换为'*.foo'
)。此后的命令大致是 eval find 。 -name“'wibble.foo'”-o“'*.bar'”。顺便说一句,如果它找到了多个匹配项,那么到最后事情会变得更加愚蠢。find
,并向其传递参数“.”、“-name”、“wibble.foo”、“-o”、“-name”和“*.bar”。find
找到“*.bar”的一个匹配项,但没有找到“wibble.foo”的匹配项。它甚至不知道您希望它查找“*.foo”。那么你能对此做些什么呢?好吧,在这种特殊情况下,添加策略性双引号 (
eval "find . $ftypes"
) 可以防止虚假通配符替换,但一般来说,最好完全避免eval
。当您需要构建命令时,数组是更好的方法(请参阅 BashFAQ #050有关更多讨论):请注意,您还可以一点一点地构建选项:
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
eval
command 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".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 avoideval
entirely. 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:
只需在该行前面添加
eval
即可强制 shell 扩展并解析命令:如果没有
eval
,则将传递'*.foo'
从字面上看,而不仅仅是*.foo
(也就是说,'
突然被认为是文件名的一部分,因此find
正在寻找以单引号开头且扩展名为foo'
)。Simply prefix the line with
eval
to force the shell to expand and parse the command:Without the
eval
, the'*.foo'
is passed on literally instead of just*.foo
(that is, the'
are suddenly considered to be part of the filename, sofind
is looking for files that start with a single quote and have an extension offoo'
).问题是,由于 $f 类型是单个带引号的值,因此 find 确实将其视为单个参数。
解决这个问题的一种方法是:
The problem is that since $ftypes a single quoted value, find does see it as a single argument.
One way around it is: