测试在命令行上失败(正确),但在脚本中成功(错误)

测试在命令行上失败(正确),但在脚本中成功(错误)

为了解决这个问题,假设 glob/a/b/c/*不产生任何匹配项。

这意味着以下测试应该失败(换句话说,它应该产生非零$?):

[[ -n /a/b/c/*(#qN) ]]

事实上是这样的如果我直接在(zsh)命令行上运行测试。但是,如果我坚持完全相同的测试在脚本中,它成功了,这不是预期的行为。

如果我使用该标志运行相同的脚本-x,则生成的跟踪将测试显示为

[[ -n '/a/b/c/*(#qN)' ]]

我不明白为什么单引号出现在 test 的参数周围;在脚本的源代码中根本没有引号。

如果 zsh 自动插入这些单引号,这将解释文本成功的原因。

问题:

  1. 为什么此表达式的命令行版本和脚本版本之间存在差异?
  2. 我需要做什么才能使脚本中的测试(正确)失败?

答案1

来自CONDITIONAL EXPRESSIONSzsh 手册的部分:

文件名生成不会在任何形式的条件参数上执行。但是,在正常 shell 扩展有效且选项 EXTENDED_GLOB 有效的任何情况下,都可以通过在字符串末尾使用 (#q) 形式的显式 glob 限定符来强制执行此操作。

换句话说,在脚本中,如果extendedglob未设置该选项,/a/b/c/*(#qN)则被视为文字字符串。

答案2

检查 glob 是否与任何文件匹配的更好的习惯用法(IMO)是:

()(($#)) /a/b/c/*(NY1)

这并不需要extendedglob的特殊情况[[ -n ...(#qN) ]]。而且由于Y1在第一场比赛就停止,效率也更高。

它的工作原理是将 glob 扩展传递给一个匿名函数,该函数的主体是(($#))算术表达式,如果该匿名函数的参数数量非零,则该算术表达式返回 true。

您可以将其扩展到以下内容:

if ()(( $# >= 3 )) /a/b/c*(NY3); then
  echo there were at least 3 matching files.
fi

相关内容