Bash: `true` 的用法

Bash: usage of `true`

在我从一位前雇员那里继承的许多脚本中,我一直看到这种模式:

if (true $SOME_VAR)&>/dev/null; then
    ...
fi

或者这个

(true $SOME_VAR)&>/dev/null || SOME_VAR="..."

true 的手册页说它总是 returns 正确,因此我一直在想,这些检查有什么意义?在第一种情况下, then 部分总是被执行,在第二种情况下,右边的部分永远不会被执行。

如果set -u(a.k.a.set -o nounset)生效,当$SOME_VAR未定义时,true $SOME_VAR会失败。因此,这是一种测试变量是否已定义的方法。

以下可能是等价的,而且更直接:

if [ "${SOME_VAR+x}" ] then
    ...
fi

或者,在分配情况下:

[ "${SOME_VAR+x}" ] || SOME_VAR="..."

如果未设置变量,+ 扩展运算符将扩展为空字符串,如果已分配,则扩展为 x(分配空字符串仍然意味着已分配)。在这种情况下,您可以将 x 替换为您想要的任何内容(空字符串除外)。

还有一个 ${SOME_VAR:+x} 变体。不同之处在于空字符串:如果为变量分配了空字符串,:+ 将扩展为空字符串(而如果分配了值,则 + 将扩展为 x,即使它是一个空字符串)。

补充jwodder's helpful answer and

  • Bash v4.2+ , 更不晦涩和更有效的 -v 运算符 可用于测试是否定义了变量[1 ](注意一定要用没有$):
    [[ -v SOME_VAR ]]

  • 较旧的Bash版本和POSIX兼容的脚本中,使用,这也更比 (true ...) 方法更有效。

  • 如果目的只是提供一个默认,如(true $SOME_VAR)&>/dev/null || SOME_VAR="..."惯用语, 使用 kojiro, also based on a parameter expansion:
    建议的 (POSIX-compliant) 技术 SOME_VAR=${SOME_VAR-...} # keep $SOME_VAR value or default to '...'
    Toby Speight suggests another POSIX-compliant variant, ${SOME_VAR=...}, which directly updates the variable with the default value, if it is undefined; however, it has the side effect of expanding to the (resulting) value - which may or may not be desired. A concise, but also slightly obscure way to suppress the expansion is to pass the expansion to the colon (null) utility (:),它扩展了,但忽略了它的参数(与出于相同目的使用 true 相比,它可能稍微不那么混乱):
    : ${SOME_VAR=...} # set $SOMEVAR to '...' only if not defined

  • 请注意上面的所有参数扩展 shown/mentioned 都有一个 变体 : 放在运算符 ,那么 不仅在变量为 未定义 时起作用,而且在定义但 为空 [ 时起作用=128=](包含空字符串):
    ${SOME_VAR:+...}${SOME_VAR:-...}${SOME_VAR:=...}
    可以说,这种变体行为是 通常更稳健的技术 ,尤其是当 set -u (set -o nunset) 是 而不是 时打开,未定义的变量扩展为 null(空)字符串。

补充jwodder的解释:

  • 使用 (...) 围绕 true $SOME_VAR 创建一个 subshell 对于这个有点晦涩的测试至关重要变量存在按预期工作。

    • 如果没有子shell,整个脚本将中止。

    • 需要一个子shell使得该技术不仅晦涩,而且效率低下(虽然偶尔使用不会很明显)。

      • 此外,如果 set -u (set -o nounset) 发生 而不是 生效,该技术将 所有 个定义的变量。
    • 对于子shell,只有子shell会中止,这反映在它到当前shell的退出代码中:1 ,如果 subshell 中止(变量不存在),0 否则。
      因此,(true ...) 命令仅在变量存在时才计算为(概念上)真。

    • &>/dev/null 禁止在变量不存在时从 subshell 发出的错误消息。

      • 顺便说一句:true 永远不会产生任何输出,所以使用 (true $SOME_VAR)2>/dev/null 就足够了(仅抑制 stderr)——这个改变使得技术 POSIX 兼容(尽管仍然不可取)。
  • 不只是 set -u (set -o nounset) 语句 脚本中会在访问时中止未定义的变量 - 使用命令行选项 -u 显式调用 bash 具有相同的效果。


[1] 自 Bash v4.3, 你也可以测试一个 array变量具有指定 index 的元素;例如:
a=( one two ); [[ -v a[0] ]] 成功,因为索引为 0 的数组元素存在;与 associative 数组类似地工作。

虽然不完全相同,

if [ x"$SOME_VAR" = x ]; then
    ...
fi

倾向于随心所欲;如果 $SOME_VAR 未定义或(差异:) 定义为零长度字符串,则 if 为真。

如果未设置 SOME_VAR 且设置了 -u,则此代码不起作用。我相信以下 bashism 有效:"${SOME_VAR-}" = "".