我如何知道是否在Bash中设置了变量?

例如,如何检查用户是否向函数提供了第一个参数?

function a {
    # if $1 is set ?
}

当前回答

要查看变量是否为非空,我使用

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

如果变量未设置或为空,则相反:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

要查看变量是否已设置(空或非空),我使用

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

如果变量未设置,则进行相反的测试:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments

其他回答

我的首选方式是:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

因此,基本上,如果一个变量被设置,它就变成了“对结果false的否定”(true=“被设置”)。

并且,如果它未设置,它将变成“对结果true的否定”(因为空结果的求值结果为true)(因此将以false=“NOT set”结束)。

(通常)正确的方式

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi

其中${var+x}是一个参数扩展,如果var未设置,则其求值结果为空,否则替换字符串x。

报价摘要

可以省略引号(所以我们可以说${var+x}而不是“${var+x}”),因为这种语法和用法保证它只扩展到不需要引号的对象(因为它要么扩展到x(不包含换行符,因此不需要引号),要么不扩展到任何对象(这导致[-z],它方便地计算出与[-z“”]相同的值(true))。

然而,虽然可以安全地省略引号,而且这并不是所有人都能立即看到的(这对第一位也是主要Bash编码者的引号解释作者来说都不明显),但有时最好使用引号[-z“${var+x}”]来编写解决方案,代价很小,可能会导致O(1)速度损失。第一作者还将此添加为使用此解决方案的代码旁边的注释,给出了此答案的URL,现在还包括了为什么可以安全地省略引号的解释。

(经常)错误的方式

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

这通常是错误的,因为它无法区分未设置的变量和设置为空字符串的变量。也就是说,如果var='',则上述解决方案将输出“var为空”。

在用户必须指定扩展名或其他财产列表的情况下,必须区分unset和“设置为空字符串”,如果不指定,则默认为非空值,而指定空字符串应使脚本使用空扩展名或附加财产列表。

然而,这种区别在每种情况下可能都不是必不可少的。在这种情况下,[-z“$var”]就可以了。

为了明确回答OP关于如何确定变量是否已设置的问题,Lionel的回答是正确的:

if test "${name+x}"; then
    echo 'name is set'
else
    echo 'name is not set'
fi

这个问题已经有很多答案,但没有一个提供真正的布尔表达式来明确区分变量值。

以下是我得出的一些明确表达:

+-----------------------+-------------+---------+------------+
| Expression in script  | name='fish' | name='' | unset name |
+-----------------------+-------------+---------+------------+
| test "$name"          | TRUE        | f       | f          |
| test -n "$name"       | TRUE        | f       | f          |
| test ! -z "$name"     | TRUE        | f       | f          |
| test ! "${name-x}"    | f           | TRUE    | f          |
| test ! "${name+x}"    | f           | f       | TRUE       |
+-----------------------+-------------+---------+------------+

顺便说一下,这些表达式是等价的:测试<表达式><=>〔<表达式>〕

其他需谨慎使用的歧义表达:

+----------------------+-------------+---------+------------+
| Expression in script | name='fish' | name='' | unset name |
+----------------------+-------------+---------+------------+
| test "${name+x}"     | TRUE        | TRUE    | f          |
| test "${name-x}"     | TRUE        | f       | TRUE       |
| test -z "$name"      | f           | TRUE    | TRUE       |
| test ! "$name"       | f           | TRUE    | TRUE       |
| test ! -n "$name"    | f           | TRUE    | TRUE       |
| test "$name" = ''    | f           | TRUE    | TRUE       |
+----------------------+-------------+---------+------------+

如果你和我一样,你所寻找的其实是

“如果设置了变量,bash仅运行命令”

你希望这是一行,所以下面这行是你想要的

仅适用于Bash 4.2或更高版本

仅在设置时运行

if [[ -v mytest ]]; then echo "this runs only if variable is set"; fi

仅在未设置时运行

if [[ ! -v mytest2 ]]; then echo "this runs only if variable is not set"; fi

阅读bash手册页的“参数扩展”部分。参数扩展并不能为正在设置的变量提供常规测试,但如果未设置参数,可以对其执行一些操作。

例如:

function a {
    first_arg=${1-foo}
    # rest of the function
}

如果分配了firstarg,则将其设置为等于$1,否则将使用值“foo”。如果绝对必须采用单个参数,并且不存在良好的默认值,则可以在未给定参数时退出并显示错误消息:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(注意:作为null命令的用法,它只是扩展其参数的值。在本例中,我们不想对$1做任何操作,如果未设置,只需退出)