【问题标题】:In Bash, how do I test if a variable is defined in "-u" mode在 Bash 中,如何测试变量是否以“-u”模式定义
【发布时间】:2012-07-06 21:58:03
【问题描述】:

我刚刚在 bash 中发现了set -u,它帮助我找到了几个以前看不见的错误。但我也有一个场景,我需要在计算一些默认值之前测试是否定义了变量。我想出的最好的方法是:

if [ "${variable-undefined}" == undefined ]; then
    variable="$(...)"
fi

有效(只要变量没有字符串值undefined)。我想知道是否有更好的方法?

【问题讨论】:

标签: bash


【解决方案1】:

从其他答案中汲取灵感,这是我发现的最适合我的方法:

if [ -z "${varname-}" ]; then
  ...
  varname=$(...)
fi

【讨论】:

  • 恕我直言问题的最佳答案。
  • 有人能解释为什么"${varname-}" "${varname:-}" 有效吗?我很惊讶第一个作品。
  • tldp.org/LDP/abs/html/parameter-substitution.html ${varname-} 是 ${varname-default} 的一种形式。如果 varname 未设置,则返回默认值,在这种情况下为空字符串。
  • 在描述各种运算符之前,手册页中的简短句子中描述了无冒号版本。很容易错过。如果varname 未设置设置为空字符串,则${varname:-foo} 扩展为foo${varname-foo} 仅在 varname 未设置时扩展为 foo
  • 今天保存了我的构建:P
【解决方案2】:

什么不起作用:测试零长度字符串

您可以通过几种方式测试未定义的字符串。使用标准测试条件如下所示:

# Test for zero-length string.
[ -z "$variable" ] || variable='foo'

但是,这不适用于 set -u

工作原理:条件赋值

或者,您可以使用条件赋值,这是一种更类似于 Bash 的方式来执行此操作。例如:

# Assign value if variable is unset or null.
: "${variable:=foo}"

由于 Bash 处理此表达式扩展的方式,您可以安全地将其与 set -u 一起使用,而不会出现“bash: variable: unbound variable”错误。

【讨论】:

  • 这并不能完全回答这个问题,因为它没有提供一种测试未定义变量的方法,只是一种设置它的方法。如果需要其他逻辑,这将无济于事。
  • 这个答案似乎更适合简单地做一个测试:unix.stackexchange.com/a/56846/18985
  • @AndrewFerrier 这个问题具体指的是如果未设置变量,则分配默认值;虽然有有效的用例来测试是否设置了变量而没有随后设置它,但这个问题不需要它。
  • 领先的:在做什么?
  • @PypeBros 显然 : 别名为 true:stackoverflow.com/questions/3224878/…
【解决方案3】:

在 bash 4.2 和更新版本中,有一个明确的方法来检查是否设置了变量,即使用 -v。 然后可以像这样实现问题中的示例:

if [[ ! -v variable ]]; then
   variable="$(...)"
fi

http://www.gnu.org/software/bash/manual/bashref.html#Bash-Conditional-Expressions

如果您只想设置变量,如果尚未设置,您最好按照以下方式进行操作:

variable="${variable-$(...)}"

请注意,这不处理已定义但为空的变量。

【讨论】:

  • 请记住,-v 直到版本 4.2 才添加到 bash
【解决方案4】:

上面的答案不是动态的,例如,如何测试定义了名称为“dummy”的变量?试试这个:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

相关:How to check if a variable is set in Bash?

【讨论】:

  • [[ -n $var ]][[ $var ]] 代替[ ! -z "$var" ] 不是更好吗? FYI Bash 自 v2 以来也支持变量间接,因此这里不需要 eval。例如。 is_var_defined(){ if (( $# != 1 )); then echo "Expected exactly one argument: variable name as string, e.g., 'my_var'" >&2; return 1; fi; [[ -n ${!1:-} ]]; }
【解决方案5】:

不幸的是,旧版本的 bash 不支持 [[ -v variable ]](至少在 Debian Squeeze 上的 4.1.5 版本中不支持)

您可以改为使用子外壳,如下所示:

if (true $variable)&>/dev/null; then
    variable="$(...)"
fi

【讨论】:

    【解决方案6】:

    在脚本的开头,您可以使用空值定义变量

    variable_undefined=""
    

    然后

    if [ "${variable_undefined}" == "" ]; then
        variable="$(...)"
    fi
    

    【讨论】:

    • 破折号不是有效的变量名字符。它是 OP 使用的参数扩展语法的一部分。
    • 那么最好用-z测试一下,看看字符串的大小是否为零
    • @coelhuedo ...但是您不能在set -u 模式下扩展未定义的变量以通过test -z 运行它,因此这个问题。
    • 我喜欢这种解决问题的方法,因为它迫使您在脚本顶部“声明”变量。
    • 这在需要使用其他进程设置的环境变量时不起作用。
    【解决方案7】:
    if [ "${var+SET}" = "SET" ] ; then
        echo "\$var = ${var}"
    fi
    

    我不知道 ${var+value} 支持多远,但它至少可以追溯到 4.1.2。旧版本没有 ${var+value},只有 ${var:+value}。不同之处在于 ${var:+value} 只会在 $var 设置为 nonempty 字符串时计算为“value”,而如果 ${var+value} 也将计算为“value”,如果$var 设置为空字符串。

    如果没有 [[ -v var ]] 或 ${var+value} 我认为您必须使用另一种方法。可能是先前答案中描述的子shell测试:

    if ( set -u; echo "$var" ) &> /dev/null; then
        echo "\$var = ${var}
    fi
    

    如果您的 shell 进程已经激活了“set -u”,那么它也将在子shell 中处于活动状态,而无需再次使用“set -u”,但是在子shell 命令中包含它可以使解决方案在以下情况下也可以工作父进程没有启用“set -u”。

    (您也可以使用“printenv”或“env”等其他进程来测试变量是否存在,但只有在导出变量时才有效。)

    【讨论】:

      猜你喜欢
      • 2011-04-20
      • 2010-10-22
      • 2011-05-07
      • 1970-01-01
      • 2012-12-02
      • 2011-11-26
      • 1970-01-01
      相关资源
      最近更新 更多