/usr/bin/find: 不能动态构建它的参数 [英] /usr/bin/find: cannot build its arguments dynamically

查看:12
本文介绍了/usr/bin/find: 不能动态构建它的参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下命令在终端中按预期以交互方式工作.

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
$

我的理解是/是 $ftypes 会在 find 有机会运行之前被 bash 扩展.在这种情况下,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.

这是怎么回事?

非常感谢.

PS:我需要动态构建文件类型列表(上面的 ftypes 变量),以便稍后在脚本中提供给 find.

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.

推荐答案

到目前为止,两个答案都推荐使用 eval,但这在引起错误方面享有当之无愧的声誉.这是您可以通过此操作获得的那种奇怪行为的示例:

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

为什么找不到文件 ./a.foo?这是因为 eval 命令是如何被解析的.bash 的解析是这样的(省略了一些不相关的步骤):

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):

  1. bash 首先查找引号(尚未找到).
  2. bash 替换变量(但不会返回并在替换值中查找引号——这首先是导致问题的原因).
  3. bash 进行通配符匹配(在这种情况下,它会查找匹配 '*.foo''*.bar' 的文件——注意它尚未解析引号,因此它只是将它们视为要匹配的文件名的一部分——并找到 'wibble.foo' 并将其替换为 '*.foo').在此之后,命令大致是 eval find .-name "'wibble.foo'" -o "'*.bar'".顺便说一句,如果它找到多个匹配项,到最后事情会变得更加愚蠢.
  4. bash 发现该行的命令是 eval,并在该行的其余部分运行整个解析过程.
  5. bash 再次进行引号匹配,这一次查找两个单引号字符串(因此它将跳过对命令的这些部分的大部分解析).
  6. bash 查找要替换的变量和要匹配的通配符等,但在命令的未引用部分中没有.
  7. 最后,bash 运行 find,将参数."、-name"、wibble.foo"、-o"、-name"和*.bar"传递给它".
  8. find 找到*.bar"的一个匹配项,但没有找到wibble.foo"的匹配项.它甚至都不知道您希望它查找*.foo".
  1. bash looks for quotes first (none found -- yet).
  2. 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).
  3. 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.
  4. bash sees that the command on the line is eval, and runs the whole parsing process over on the rest of the line.
  5. bash does quote matching again, this time finding two single-quoted strings (so it'll skip most parsing on those parts of the command).
  6. bash looks for variables to substitute and wildcards to matching, etc, but there aren't any in the unquoted sections of the command.
  7. Finally, bash runs find, passing it the arguments ".", "-name", "wibble.foo", "-o", "-name", and "*.bar".
  8. find finds one match for "*.bar", but no match for "wibble.foo". It never even knows you wanted it to look for "*.foo".

那么你能做些什么呢?好吧,在这种特殊情况下,添加战略双引号 (eval "find . $ftypes") 可以防止虚假的通配符替换,但通常最好完全避免 eval.当您需要构建命令时,数组是一种更好的方法(请参阅 BashFAQ #050更多讨论):

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')

这篇关于/usr/bin/find: 不能动态构建它的参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆