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

Har*_*rry 3 bash interactive find

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

$ find . -name '*.foo' -o -name '*.bar'
./a.foo
./b.bar
$
Run Code Online (Sandbox Code Playgroud)

但是,如果我这样做,我就没有结果!

$ ftypes="-name '*.foo' -o -name '*.bar'"
$ echo $ftypes
-name '*.foo' -o -name '*.bar'
$ find . $ftypes
$
Run Code Online (Sandbox Code Playgroud)

我的理解是,在有机会跑之前,它$ftypes会被扩展.在这种情况下,该方法也应该有效.bashfindftypes

这里发生了什么?

提前谢谢了.

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

Gor*_*son 7

到目前为止,这两个答案都建议使用eval,但是它因引起错误而享有当之无愧的声誉.以下是您可以通过以下方式获得的奇怪行为示例:

$ touch a.foo b.bar "'wibble.foo'"
$ ftypes="-name '*.foo' -o -name '*.bar'"
$ eval find . $ftypes
./b.bar
Run Code Online (Sandbox Code Playgroud)

为什么没找到文件./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".
  8. find找到一个匹配"*.bar",但不匹配"wibble.foo".它甚至都不知道你想要它寻找"*.foo".

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

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

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

$ ftypes=(-name '*.foo')
$ ftypes+=(-o -name '*.bar')
$ ftypes+=(-o -name '*.baz')
Run Code Online (Sandbox Code Playgroud)