以下命令在终端中以交互方式按预期工作。
$ 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
$
我的理解是$ftypes
在bash
有机会运行之前find
会扩展ftypes
。在这种情况下,ftypes
方法也应该有效。
这里发生了什么?
非常感谢提前。
PS:我需要动态构建一个文件类型列表(上面的find
变量),稍后将在脚本中提供给{{1}}。
答案 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的解析就是这样的(省略了一些不相关的步骤):
'*.foo'
和'*.bar'
的文件 - 请注意它没有解析引号,所以它只是将它们视为文件名的一部分匹配 - 并找到'wibble.foo'
并将其替换为'*.foo'
)。在此之后,命令大致为eval find . -name "'wibble.foo'" -o "'*.bar'"
。顺便说一句,如果它找到了多个匹配项,那么到最后它们就会变得更加愚蠢。eval
,并在该行的其余部分运行整个解析过程。find
,传递参数“。”,“ - name”,“wibble.foo”,“ - o”,“ - name”和“* .bar”。< / LI>
find
找到一个匹配“* .bar”,但不匹配“wibble.foo”。它甚至都不知道你想要它寻找“* .foo”。那么你能做些什么呢?好吧,在这种特殊情况下,添加策略性双引号(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