60

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

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

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

4

7 回答 7

64

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

if [ -z "${varname-}" ]; then
  ...
  varname=$(...)
fi
于 2012-07-06T20:40:11.187 回答
34

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

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

# 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”错误。

于 2012-07-06T12:38:27.177 回答
11

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

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

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

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

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

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

于 2013-06-05T17:59:03.697 回答
5

上面的答案不是动态的,例如,如何测试定义了名称为“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.
}

相关:如何检查是否在 Bash 中设置了变量?

于 2013-11-09T08:48:53.077 回答
1

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

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

if (true $variable)&>/dev/null; then
    variable="$(...)"
fi
于 2014-04-11T10:35:02.613 回答
0

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

variable_undefined=""

然后

if [ "${variable_undefined}" == "" ]; then
    variable="$(...)"
fi
于 2012-07-06T12:34:33.387 回答
0
if [ "${var+SET}" = "SET" ] ; then
    echo "\$var = ${var}"
fi

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

如果没有 [[ -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”来测试变量是否存在,但只有在导出变量时才会起作用。)

于 2016-03-04T00:49:55.483 回答