如何检查Bash中是否设置了变量?


1565

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

例如,如何检查用户是否将第一个参数赋予函数?

function a {
    # if $1 is set ?
}

12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
詹斯

210
寻求解决方案的人员的注意事项:此问题有很多高度评价的答案,它们回答了“变量为非空”问题。下面的Jens和Lionel的答案中提到了更多的校正解决方案(“变量集”)。
内森·基德

8
拉塞尔·哈蒙(Russell Harmon)和西姆斯(Seamus)的-v测试也是正确的,尽管这似乎仅适用于新版本的,bash并且不能跨外壳移植。
Graeme 2014年

5
正如@NathanKidd指出的那样,Lionel和Jens提供了正确的解决方案。寻求帮助,您应该将接受的答案切换为其中之一。
加勒特2014年

3
...或者我们之间比较有洞察力的人可能会否定不正确的答案,因为@prosseek无法解决问题。
dan3

Answers:


2297

(通常)正确的方法

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 ],可以方便地求出相同的值(true [ -z "" ]))。

但是,尽管可以安全地省略引号,并且所有人都不会立即看到引号(对于引号解释的第一作者,他也是主要的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 is blank”。

在用户必须指定扩展名或其他属性列表且未指定属性默认情况下默认为非空值的情况下,未设置和“设置为空字符串”之间的区别至关重要。使脚本使用空扩展名或其他属性列表。

但是,这种区分可能并非在每种情况下都必不可少。在那种情况下 [ -z "$var" ]就可以了。


29
@Garrett,您的编辑使此答案不正确,${var+x}是可以使用的正确替代方法。使用所[ -z ${var:+x} ]产生的结果与相同[ -z "$var" ]
Graeme

11
这行不通。无论是否将var设置为值,我都会得到“未设置”(用“ unset var”清除,“ echo $ var”清除不会产生任何输出)。
布伦特212年

10
对于解决方案的语法$ {parameter + word},官方手册部分为gnu.org/software/bash/manual/…;但是,有一个错误,它并没有很清楚地提到此语法,而是说quote(省略冒号[“:”]只对未设置的参数进行测试。.. $ {parameter:+ word} [方法]如果参数为null或未设置,则不替换任何内容,否则替换单词的扩展。)引用的参考文献pubs.opengroup.org/onlinepubs/9699919799/utilities/…(很好知道)此处的文档更加清晰。
命运建筑师

7
当您使用多个表达式时,例如[ -z "" -a -z ${var+x} ]bash: [: argument expected在bash 4.3-ubuntu中获取(但在zsh中则不需要),则看起来引号是必需的。我真的不喜欢使用在某些情况下可以使用的语法的答案。
FauxFaux 2015年

41
使用简单[ -z $var ]不比省略引号更“错误”。无论哪种方式,您都在对输入进行假设。如果您可以将空字符串视为未设置,则只[ -z $var ]需要一个。
nshew

908

要检查非空/非零字符串变量,即是否已设置,请使用

if [ -n "$1" ]

与的相反-z。我发现自己使用的-n次数超过-z

您将使用它像:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi

86
我通常喜欢[[ ]][ ],因为[[ ]]是功能更强大,并导致在某些情况下少的问题(见这个问题,两者之间的差别的解释)。该问题专门要求提供bash解决方案,并且未提及任何可移植性要求。
流量

18
问题是如何看待是否设置了变量。然后,您不能假定设置变量。
HelloGoodbye 2013年

7
我同意,[]尤其适用于shell(非bash)脚本。
横街2014年


63
请注意,这-n是默认测试,因此更简单[ "$1" ][[ $1 ]]也可以。另请注意,带[[ ]] 引号是不必要的
tne 2014年

477

这是测试参数是否为unset,为空(“ Null”)设置为值的方法

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

来源:POSIX:参数扩展

在所有显示为“替换”的情况下,表达式都将替换为显示的值。在所有显示为“ assign”的情况下,都会为参数分配该值,该值也会替换表达式。

为了展示这一点:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+

5
@HelloGoodbye是的,它确实起作用:echo set foo; echo ${1:-set but null or unset}“ foo”; set --; echo ${1:-set but null or unset}回声设置但为空...
Jens

4
@HelloGoodbye可以使用uh设置位置参数set:-)
Jens

95
这个答案很混乱。关于如何使用此表的任何实际示例?
本·戴维斯

12
@BenDavis在POSIX标准的链接中对此进行了详细说明,该链接引用了该表的来源。我在几乎所有编写的脚本中使用的一种构造是: ${FOOBAR:=/etc/foobar.conf}为变量设置默认值(如果未设置或为null)。
詹斯

1
parameter是任何变量名称。 word是根据要使用的表中的语法以及变量$parameter是设置,设置为null还是未设置来替换的字符串。不是使事情变得更复杂,而是word还可以包含变量!这对于传递以字符分隔的可选参数非常有用。例如: 如果设置${parameter:+,${parameter}}了逗号,,$parameter但输出一个逗号分隔但不为null。在这种情况下,word,${parameter}
TrinitronX

260

虽然此处介绍的大多数技术都是正确的,但bash 4.2支持对变量(man bash)的存在进行实际测试,而不是测试变量的值。

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

值得注意的是,与许多其他方法(例如使用)不同,该方法在set -u/ set -o nounset模式下检查未设置的变量时不会导致错误[ -z


9
在bash 4.1.2中,无论是否设置了变量,[[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'

32
bash 4.2中添加了对“ test”内置函数的“ -v”参数。
Ti Strga 2014年

19
-v参数是对-u shell选项(名词集)的补充。启用“ nounset”(设置-u)后,如果外壳不是交互式的,则外壳将返回错误并终止。$#非常适合检查位置参数。但是,命名变量需要除破坏之外的其他方式才能将其标识为未设置。不幸的是,此功能来得太晚了,因为许多功能都必须与早期版本兼容,在这种情况下,他们将无法使用它。唯一的其他选择是使用技巧或“黑客”来绕过它,如上所示,但这是最不雅观的。
osirisgothra 2014年

2
注意,这不适用于已声明但未设置的变量。例如declare -a foo
miken32 '18

17
这就是为什么我在接受/投票最高的答案之后继续阅读的原因。

176

执行此操作的方法有很多种,其中之一是:

if [ -z "$1" ]

如果$ 1为null或未设置,则此操作成功


46
未设置的参数和具有空值的参数之间存在差异。
chepner 2013年

21
我只是想学究一点,并指出这是对该问题提出的相反答案。这些问题询问是否设置了变量,而不是未设置变量。
Philluminati

47
[[ ]]只是可移植性陷阱。if [ -n "$1" ]应该在这里使用。
詹斯

73
这个答案是不正确的。该问题要求一种方法来判断变量是否已设置,而不是是否将其设置为非空值。给定foo=""$foo具有一个值,但-z只会报告其为空。而-z $foo将炸毁,如果你有set -o nounset
基思·汤普森

4
它取决于“变量集”的定义。如果要检查变量是否设置为非零字符串,则mbranning答案正确。但是,如果您要检查变量是否已声明但未初始化(即foo=),则Russel的答案是正确的。
流量

76

我总是在另一个答案中发现POSIX表很慢,所以这是我的看法:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

请注意,每个组(有或没有前冒号)都具有相同的设置和未设置案例,因此唯一不同的是如何处理案例。

对于前面的冒号,和未设置的情况是相同的,因此我将在可能的情况下使用它们(即,使用:=,而不仅仅是=,因为空情况不一致)。

标题:

  • set表示VARIABLE非空(VARIABLE="something"
  • 表示VARIABLE为空/空(VARIABLE=""
  • 未设置意味着VARIABLE不存在(unset VARIABLE

值:

  • $VARIABLE 表示结果是变量的原始值。
  • "default" 表示结果是提供的替换字符串。
  • "" 表示结果为空(空字符串)。
  • exit 127 表示脚本停止执行,退出代码为127。
  • $(VARIABLE="default")里指的是该变量的初始值提供被分配给将来使用该变量的替换字符串。

1
您只修复了实际的编码错误(使用变量时发生不必要的间接调用的实例)。我进行了修改,以解决美元在解释说明中有所不同的其他几种情况。顺便说一句,所有shell变量(数组除外)都是字符串变量;它们可能包含可以解释为数字的字符串,这可能只是发生。谈论字符串只会使信息模糊。引号与字符串无关,它们只是转义的替代方法。VARIABLE=""可以写成VARIABLE=。尽管如此,前者更具可读性。
Palec

感谢您提供的表格,它非常有用,但是如果未设置或为空,我将尝试退出脚本。但退出代码我得到的是1不是127
宇航员

64

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

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

我也已经使用了一段时间了。只是以减少的方式:[ $isMac ] && param="--enable-shared"

@Bhaskar我想这是因为这个答案已经提供了基本相同的答案(使用${variable+x}获得x当且仅当$variable是集)几乎有一半去年同期。此外,它还有更多详细信息解释其正确性。
Mark Haferkamp '18

${variable+x}可读性较差(且显而易见)
knocte

35

在现代版本的Bash(我认为是4.2或更高版本;我不确定)上,我会尝试这样做:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi

5
还要注意,这[ -v "$VARNAME" ]不是不正确的,而只是执行不同的测试。假设VARNAME=foo; 然后检查是否foo设置了名为的变量。
chepner 2014年

5
对于那些需要便携性的用户,请注意这些-v内容不符合POSIX
kzh 2015年

如果得到[: -v: unexpected operator,则必须确保使用bash而不是sh
koppor

25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

虽然对于参数,通常我认为最好测试$#,这是参数的数量。

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

1
第一个测试是落后的;我认为您想要[ "$1" != "" ](或[ -n "$1" ])...
戈登·戴维森

10
如果将$1其设置为空字符串,则将失败。
HelloGoodbye

2
答案的第二部分(计数参数)是一个有用的解决方法,因为当答案的第一部分$1设置为空字符串时不起作用。
马克·贝里

如果set -o nounset打开,它将失败。
Flimm

21

注意

由于bash标签的缘故,我给出了一个以Bash为重点的答案。

简短答案

只要您仅在Bash中处理命名变量,此函数就应该始终告诉您变量是否已设置,即使它是一个空数组。

variable-is-set() {
    declare -p "$1" &>/dev/null
}

为什么这样

在Bash中(至少可以追溯到3.0),如果var是声明的/设置的变量,则declare -p var输出declare命令,将变量设置var为当前类型和值,并返回状态代码0(成功)。如果var未声明,则declare -p var输出一条错误消息stderr并返回状态码1。使用&>/dev/null,将常规stdoutstderr输出重定向到/dev/null,从不被看到,并且不更改状态码。因此,该函数仅返回状态码。

为什么其他方法(有时)在Bash中失败

  • [ -n "$var" ]仅检查是否${var[0]}为非空。(在Bash中,$var与相同${var[0]}。)
  • [ -n "${var+x}" ]仅检查是否${var[0]}已设置。
  • [ "${#var[@]}" != 0 ]仅检查是否设置了至少一个索引$var

当此方法在Bash中失败时

这仅适用于命名的变量(包括$_),而不是某些特殊变量($!$@$#$$$*$?$-$0$1$2,...,和任何我可能已经忘记了)。由于这些都不是数组,因此POSIX样式[ -n "${var+x}" ]适用于所有这些特殊变量。但是要当心将其包装在函数中,因为在调用函数时许多特殊变量会更改值/存在。

外壳兼容性说明

如果您的脚本具有数组,并且您正尝试使其与尽可能多的Shell兼容,那么可以考虑使用typeset -p而不是declare -p。我读过ksh仅支持前者,但无法对其进行测试。我确实知道Bash 3.0+和Zsh 5.5.1都支持typeset -pdeclare -p,不同之处仅在于其中一个可以替代另一个。但是我没有测试过这两个关键字以外的差异,也没有测试过其他shell。

如果您需要脚本与POSIX sh兼容,则不能使用数组。没有数组,[ -n "{$var+x}" ]工作。

Bash中不同方法的比较代码

此功能取消设置变量varevalS中的传递代码,运行测试,以确定是否var由设定evald代码,最后示出了用于不同的测试所得到的状态代码。

我跳过了test -v var[ -v var ]并且[[ -v var ]]因为它们产生与POSIX标准相同的结果[ -n "${var+x}" ],同时需要Bash 4.2+。我也跳过了,typeset -p因为它与declare -p我测试过的shell 相同(Bash 3.0到5.0,以及Zsh 5.5.1)。

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

测试用例代码

请注意,如果未将变量声明为关联数组,则Bash将非数值数组索引视为“ 0”,则测试结果可能是意外的。而且,关联数组仅在Bash 4.0+中有效。

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

测试输出

该测试在助记符标题行对应于[ -n "$var" ][ -n "${var+x}" ][ "${#var[@]}" != 0 ],和declare -p var,分别。

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

摘要

  • declare -p var &>/dev/null (至少100%?)至少从3.0开始就可以可靠地测试Bash中的命名变量。
  • [ -n "${var+x}" ] 在符合POSIX的情况下是可靠的,但不能处理数组。
  • 还存在其他测试,用于检查变量是否为非空,以及检查其他外壳程序中已声明的变量。但是这些测试都不适合Bash和POSIX脚本。

3
在我尝试过的答案中,这个(declare -p var &>/dev/null)是唯一有效的答案。谢谢!
user1387866 '19

2
@ mark-haferkamp我试图编辑您的答案以在函数代码中添加关键的前置“ /”,以便它可以读取,... &> /dev/null但这样我就不允许我进行单个字符编辑🙄(必须大于6个字符-甚至尝试添加空格但没有用)。另外,可能值得更改该函数以切换到$1名为变量的示例(例如OUTPUT_DIR),因为如您所指出的,特殊变量(如)$1在这里不起作用。无论如何,这是一项重要的编辑,否则代码将寻找在名为null的相对目录中创建一个名为的文件dev。顺便说一句-好答案!
joehanna

同样,使用不带$前缀的变量名称也可以:declare -p PATH &> /dev/null返回0,其中declare -p ASDFASDGFASKDF &> /dev/null返回1。(在bash 5.0.11(1)-release上)
joehanna

1
做得好!谨慎的1个字:declare var声明VAR变量,但它并没有将其设置在以下方面set -o nounset:测试declare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche

@joehanna感谢您发现那个失踪者/!我已解决此问题,但我认为逻辑很混乱,无法保证功能,尤其是考虑到@xebeche的评论。@xebeche这对我的回答很不方便。。。看来我必须尝试类似declare -p "$1" | grep =或的方法test -v "$1"
Mark Haferkamp '19

19

对于那些希望在脚本中使用 以下命令检查未设置或为空的对象set -u

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

常规[ -z "$var" ]检查将以var; unbound variableif 失败,set -u但如果未设置而未失败则[ -z "${var-}" ] 扩展为空字符串var


您缺少一个冒号。应该是${var:-},不是${var-}。但是在Bash中,我可以确认即使没有冒号,它也能正常工作。
Palec

3
${var:-}${var-}意味着不同的事情。${var:-}将扩大仿佛空字符串var未设置或为空并且${var-}将扩大到仅如果取消设置空字符串。
RubenLaguna

刚学到新东西,谢谢!省略冒号只会对未设置的参数进行测试。换句话说,如果包含冒号,则运算符会测试参数是否存在以及其值是否不为null;如果省略了冒号,则运算符仅测试是否存在。这里没有什么区别,但很高兴知道。
Palec

17

如果未设置,您要退出

这对我有用。如果未设置参数,我希望脚本退出并显示错误消息。

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

运行时返回错误

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

仅检查,不退出-空和未设置无效

如果您只想检查值set = VALID或unset / empty = INVALID,请尝试此选项。

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

或者,即使是简短测试;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

仅检查,不退出-仅空为无效

这就是问题的答案。如果只想检查值set / empty = VALID或unset = INVALID,请使用此选项。

注意,“ ..- 1}”中的“ 1”无关紧要,可以是任何数字(例如x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

简短测试

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

我将这个答案献给@ mklement0(评论),他要求我准确回答这个问题。

参考http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02


15

要检查变量是否设置为非空值,请使用[ -n "$x" ],如其他变量所示。

在大多数情况下,以与未设置的变量相同的方式对待具有空值的变量是一个好主意。但是你可以区分这两种,如果你需要:[ -n "${x+set}" ]"${x+set}"膨胀到set如果x如果被设置,为空字符串x未设置)。

要检查是否已传递参数,请测试$#一下,它是传递给函数(或不在函数中时传递给脚本)的参数数量(请参阅Paul的答案)。


14

阅读bash手册页的“参数扩展”部分。参数扩展不能为设置的变量提供一般测试,但是如果没有设置参数,您可以对它做几件事。

例如:

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

将被设置为first_arg等于$1是否已分配,否则将使用值“ foo”。如果a绝对必须使用一个参数,并且没有良好的默认值,则在不指定参数的情况下可以退出并显示错误消息:

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

(请注意,使用:as作为空命令只会扩展其参数的值。$1在此示例中,我们不想执行任何操作,如果未设置,则退出)


1
我很困惑,没有其他答案提到简单而优雅: ${var?message}
2015年

你从哪儿拿来的?精彩!这只是工作!而且又短又优雅!谢谢!
罗杰

13

在bash中,您可以-v[[ ]]内置函数中使用:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR

8

使用[[ -z "$var" ]]是了解是否设置了变量的最简单方法,但是该选项-z无法区分未设置的变量和设置为空字符串的变量:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

最好根据变量的类型进行检查:env变量,参数或常规变量。

对于环境变量:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

对于参数(例如,检查parameter的存在$5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

对于常规变量(使用辅助函数,以一种简洁的方式进行操作):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

笔记:

  • $#为您提供位置参数的数量。
  • declare -p为您提供作为参数传递的变量的定义。如果存在,则返回0,否则返回1,并显示错误消息。
  • &> /dev/null抑制输出declare -p而不影响其返回码。

5

set -u启用“重击”选项时,以上答案无效。而且,它们不是动态的,例如,如何测试定义了名称为“ 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中,如何测试是否在“ -u”模式下定义了变量


1
我非常想知道这个答案为什么被拒绝...对我来说很棒。
LavaScornedOven 2014年

在Bash中,您可以不使用eval来执行相同操作:更改eval "[ ! -z \${$1:-} ]"为间接评估:[ ! -z ${!1:-} ];

@BinaryZebra:有趣的想法。我建议您发布一个单独的答案。
kevinarpe 2015年

使用“ eval”会使此解决方案容易受到代码注入的影响:$ is_var_defined'x}]; 回声“陷阱”>&2; #'陷阱
gotcha tetsujin


4

我的首选方式是这样的:

$ 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=“未设置”结束)。



1

在shell中-z,如果字符串的长度为零,则可以使用True运算符。

MY_VAR如果未设置默认值,则可以使用简单的单行代码设置默认值,否则可以显示以下消息:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi

$1$2并且$N始终设置为。对不起,这是失败的。

我尝试了一下,但没有成功,也许我的bash版本缺少对此的支持。Idk,对不起,如果我错了。:)

0

如果您想检查任何内容,我发现了一个(更好)更好的代码来执行此操作 $@

如果[[$ 1 =“”]]
然后
  回显'$ 1为空'
其他
  回声“ $ 1已填满”
科幻

为什么这一切呢?一切都在$@存在于猛砸,但默认情况下它是空白,所以test -ztest -n不能帮助你。

更新:您还可以计算参数中的字符数。

如果[$ {#1} = 0]
然后
  回显'$ 1为空'
其他
  回声“ $ 1已填满”
科幻

我知道,仅此而已。如果有东西是空的,你会得到一个错误!抱歉。:P

0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

11
欢迎使用StackOverflow。尽管您的回答受到赞赏,但最好提供的不仅仅是代码。您可以在答案中添加理由还是进行一些简短说明?有关扩展答案的其他想法,请参见此页面
Celeo 2015年

0

这是我每天使用的:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

在Linux和Solaris(低至bash 3.0)下,此方法效果很好。

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1

1
由于Bash 2.0+有间接扩展可用。您可以将test -n "$(eval "echo "\${${1}+x}"")"等效的长而复杂的(也包括评估)替换为:[[ ${!1+x} ]]

0

我喜欢辅助功能来隐藏bash的原始细节。在这种情况下,这样做会增加更多(隐藏的)粗度:

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

因为我最初在此实现中遇到错误(受到Jens和Lionel的回答启发),所以我想出了一个不同的解决方案:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

我发现它更简单,更轻松,更易于理解/记住。测试用例表明它是等效的:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

测试用例还表明,local varNOT声明VAR(除非后跟“=”)。在相当长的一段时间里,我以为我以这种方式声明了变量,只是发现现在我只是表达了自己的意图...我猜这是一个禁忌。

IsDeclared_Tricky xyz:1
IsDeclared_Tricky foo:1
IsDeclared_Tricky条:0
IsDeclared_Tricky baz:0
IsDeclared xyz:1
IsDeclared foo:1
IsDeclared bar:0
IsDeclared baz:0

奖励:用例

我主要使用此测试以某种 “优雅”且安全的方式(几乎类似于接口...)为函数提供(并返回)参数:

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

如果全部调用都需要声明变量:

你好,世界!

其他:

错误:未声明变量:outputStr


0

略过所有答案后,这也有效:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

如果您没有放置SOME_VAR我所拥有的东西$SOME_VAR,它将设置为空值;$这项工作是必要的。


当你将无法正常工作set -u的效果,它将打印unboud variable错误
迈克尔Heuberger

-1

检查是否设置了变量

var=""; [[ $var ]] && echo "set" || echo "not set"

3
如果将$var其设置为空字符串,则将失败。
HelloGoodbye

-1

如果您希望测试某个变量是否绑定或未绑定,即使您已启用名词集选项,该方法也能很好地工作:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi

我想你的意思set -o nounset不是set -o noun set。这仅适用于已export编辑的变量。它还以一种笨拙的撤消方式更改您的设置。
基思·汤普森

-1

我总是使用这一代码,是基于这样的事实,即任何首次看到该代码的人都容易理解:

if [ "$variable" = "" ]
    then
    echo "Variable X is empty"
fi

并且,如果要检查是否不为空;

if [ ! "$variable" = "" ]
    then
    echo "Variable X is not empty"
fi

而已。


4
该问题询问如何检查是否设置了变量,而不是变量是否为空。
HelloGoodbye

您可能需要使用[[ ... ]]而不是[ ... ]。后者实际上是一个外部命令,对于是否设置了变量没有任何可见性。
solidsnack
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.