为什么这个 bash 条件检查适用于 [[ -n .. ]] 而不是 [ -n .. ]?

为什么这个 bash 条件检查适用于 [[ -n .. ]] 而不是 [ -n .. ]?

我有一个无法检测零长度字符串的脚本,该脚本[ -n $value ]在 bash 条件表达式中使用,即

#!/usr/bin/env bash

value=""
if [ -n $value ]
then
    echo "value is non-zero"
fi

结果

值非零

如果我使用[[ -n $value ]]它有效,即

#!/usr/bin/env bash

value=""
if [[ -n $value ]]
then
    echo "value is non-zero"
fi

using[[不会产生预期的输出。从手册页:

   [[ expression ]]
      Return a status of 0 or 1 depending on the evaluation of the conditional expression expression.  Expressions are composed of the pri‐
      maries described below under CONDITIONAL EXPRESSIONS.  Word splitting and pathname expansion are not performed on the  words  between
      the  [[  and ]]; tilde expansion, parameter and variable expansion, arithmetic expansion, command substitution, process substitution,
      and quote removal are performed.  Conditional operators such as -f must be unquoted to be recognized as primaries.

我无法从中解释这种行为。
为什么[[检测到零长度字符串但[没有检测到?

答案1

这是因为[[需要一个表达,并[论点它转化为表达

[[是语法 - 它不是内置命令照原样[,而是[[一个复合命令并且 更类似于{(比它更类似于[

无论如何,因为[[被解析旁边 $expansions,理解空值扩展和缺失操作数之间的区别并不困难。

[然而,这是例行运行所有命令行扩展都已经发生,并且当它计算其表达式时,$null_expansion已经被扩展为空,所以它收到的只是[ -n ]不得是一个有效的表达式。[指定对于非空单参数情况返回 true - 就像-n这里一样 - 但是规格非常相同继续说……

这两个命令:

  test "$1"
  test ! "$1"

无法在某些历史系统上可靠地使用。如果使用此类字符串表达式并将其$1扩展为!(或已知的一元主表达式,则会出现意外结果(例如-n。更好的构造是:

  test -n "$1"
  test -z "$1" 

两种形式都有优点和缺点。[可以构造表达式在......之外扩展,等等:

[ "-${z:-n}" "$var" ]

...可能是构建测试的完全有效的方法,但不适用于:

[[ "-${z:-n}" "$var" ]]

……这是一个无意义的命令。差异完全是由于运行测试的命令行解析点造成的。

答案2

变量不必用双引号引起来,[[ ]]但应该用引号引起来[ ]

不带引号,如果$value是空字符串,[ -n $value ]则与 完全相同[ "-n" ]"-n"是一个字符串测试,并且计算结果为非空,因此为 true,因此测试成功。

(为什么?因为-n STRING和又名STRING相同- 它们都是字符串非空的测试)[test

进一步的实验表明,这似乎是所有单操作数测试的情况,包括文件运算符 - 如果没有操作数,[ ]则将单个参数视为字符串测试。如果这不是剧本作者几十年来所依赖的历史事实,那么这可能会被认为是一个错误并已修复 - 并且更改它会破坏无数现有的脚本。

带引号的[ -n "$value" ]与 完全相同[ -n "" ],因此测试失败。

[[ ... ]]对于变量的引用更加宽容——引用是可选的,无论有没有引号,它的工作原理都是一样的。

相关内容