Bash:使用'true`

Eli*_*igo 21 bash conditional if-statement

在我从一名前员工继承的许多脚本中,我一直看到这种模式:

if (true $SOME_VAR)&>/dev/null; then
    ...
fi
Run Code Online (Sandbox Code Playgroud)

或者这个

(true $SOME_VAR)&>/dev/null || SOME_VAR="..."
Run Code Online (Sandbox Code Playgroud)

人工页面true说它总是返回true,因此我一直在想,这些检查有什么意义?在第一种情况下,then部件始终执行,在第二种情况下,从不执行右手部件.

jwo*_*der 24

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

  • @kojiro括号导致`true $ SOME_VAR`在子shell中执行,因此如果未设置变量,则只有子shell会中止. (4认同)
  • @EliKorvigo我建议你用不需要子shell测试的东西替换这个表达式是设置var.`SOME_VAR ="$ {SOME_VAR:-default}"想到了. (3认同)
  • @kojiro:鉴于这是Bash,那么``$ {SOME_VAR:= default}"`,使用得当,将实现这一点.同意问题中的模式是丑陋和脆弱的! (3认同)
  • 啊哈!它确实有效,但它非常混乱.您必须使整个子shell无法捕获该变量未设置.在我的测试中,我忘记了parens. (2认同)

mkl*_*nt0 14

为了补充jwodder的有用答案Fred的有用答案:

  • Bash v4.2 +中 ,可以使用不那么模糊和更有效的-v运算符测试变量是否被定义[1](注意不必 $使用):
    [[ -v SOME_VAR ]]

  • 较旧的Bash版本和符合POSIX的脚本中,使用Fred的基于参数扩展的方法,这种方法也比(true ...)方法更有效.

  • 如果意图是简单地提供默认,如在(true $SOME_VAR)&>/dev/null || SOME_VAR="..."习语中,使用kojiro建议的(POSIX兼容)技术,也基于参数扩展:
    SOME_VAR=${SOME_VAR-...} # keep $SOME_VAR value or default to '...'
    Toby Speight建议另一个符合POSIX的变体${SOME_VAR=...},它直接更新变量如果未定义,则使用默认值; 然而,它具有扩展到(结果)值的副作用- 这可能是也可能不是所希望的.抑制扩展的简洁但又略微模糊的方法是将扩展传递给冒号(null)实用程序(:),该实用程序扩展,但忽略其参数(与true用于相同目的相比,它可能稍微有点混乱):
    : ${SOME_VAR=...} # set $SOMEVAR to '...' only if not defined

  • 注意,示出的所有参数扩展/上述具有变体其放置:在操作者面前,然后不仅作用在可变是未定义的,而且当它被定义,但(包含空字符串): ,,
    ${SOME_VAR:+...} 可以说,该变型行为通常是更强大的技术,特别是当没有打开when ()时,未定义的变量会扩展为null(空)字符串.${SOME_VAR:-...}${SOME_VAR:=...}
    set -uset -o nunset

要添加到jwodder的解释:

  • 使用(...)around true $SOME_VAR来创建子shell对于这种有些模糊的测试,对于变量存在按预期工作是至关重要的.

    • 没有子shell,整个脚本将中止.

    • 对子壳的需求使得该技术不仅模糊不清,而且效率低下(尽管偶尔使用时不会引起注意).

      • 此外,如果set -u(set -o nounset)恰好没有生效,该技术会将所有变量视为已定义.
    • 使用子shell,只有子shell中止,这在其退出代码中反映到当前shell 1:,如果子shell中止(变量不存在),0否则.
      因此,(true ...)如果变量存在,则命令仅评估(概念上)true.

    • &>/dev/null 如果变量不存在,则禁止从子shell中发出的错误消息.

      • 旁白:true永远不会产生任何输出,因此它足以使用(true $SOME_VAR)2>/dev/null(仅抑制stderr) - 这种改变使得技术符合POSIX(尽管仍然不可取).
  • 这不仅是set -u(set -o nounset)语句里面是打开的访问的情况下中止未定义变量的脚本-调用bash与命令行选项明确-u有同样的效果.


[1]从Bash v4.3开始,您还可以测试数组变量是否具有指定索引的元素; 例如:
a=( one two ); [[ -v a[0] ]]成功,因为0存在索引的数组元素; 与关联数组类似.


Fre*_*red 6

以下可能是等效的,更直接的:

if [ "${SOME_VAR+x}" ] then
    ...
fi
Run Code Online (Sandbox Code Playgroud)

或者,在作业案例中:

[ "${SOME_VAR+x}" ] || SOME_VAR="..."
Run Code Online (Sandbox Code Playgroud)

+膨胀操作扩展到一个空字符串,如果该变量被复位,并且x,如果它被分配(分配一个空字符串仍然意味着分配的).在这种情况下,您可以替换x任何您想要的(除了空字符串).

还有一个${SOME_VAR:+x}变种.区别在于空字符串::+如果为变量分配了空字符串,则+扩展为空字符串(x如果分配了值,则扩展为空字符串,即使它是空字符串).

  • @EliKorvigo:它具有POSIX意义上的便携性,这可能是最重要的.请注意,问题中的代码使用Bashisms. (3认同)