/ usr / bin / find:无法动态构建其参数

时间:2012-02-16 09:28:33

标签: bash find interactive

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

$ 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
$

我的理解是$ftypesbash有机会运行之前find会扩展ftypes。在这种情况下,ftypes方法也应该有效。

这里发生了什么?

非常感谢提前。

PS:我需要动态构建一个文件类型列表(上面的find变量),稍后将在脚本中提供给{{1}}。

3 个答案:

答案 0 :(得分:7)

到目前为止,这两个答案都建议使用eval,但这有助于造成错误。以下是您可以通过以下方式获得的奇怪行为的示例:

$ touch a.foo b.bar "'wibble.foo'"
$ ftypes="-name '*.foo' -o -name '*.bar'"
$ eval find . $ftypes
./b.bar

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

  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”。< / LI>
  8. find找到一个匹配“* .bar”,但不匹配“wibble.foo”。它甚至都不知道你想要它寻找“* .foo”。
  9. 那么你能做些什么呢?好吧,在这种特殊情况下,添加策略性双引号(eval "find . $ftypes")可以防止虚假的通配符替换,但通常最好完全避免使用eval。当您需要构建命令时,数组是一种更好的方法(有关更多讨论,请参阅BashFAQ #050):

    $ ftypes=(-name '*.foo' -o -name '*.bar')
    $ find . "${ftypes[@]}"
    ./a.foo
    ./b.bar
    

    请注意,您也可以逐位构建选项:

    $ ftypes=(-name '*.foo')
    $ ftypes+=(-o -name '*.bar')
    $ ftypes+=(-o -name '*.baz')
    

答案 1 :(得分:2)

简单地在行前加eval以强制shell展开并解析命令:

eval find . $ftypes

如果没有eval'*.foo'会按字面意思传递,而不仅仅传递*.foo(也就是说,'突然被认为是文件名的一部分,所以find正在查找以单引号开头并且扩展名为foo')的文件。

答案 2 :(得分:1)

问题在于,由于$ ftypes是单个引用值,因此find确实将其视为单个参数。

解决方法之一是:

$ eval find . $ftypes