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


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

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

function a {
    # if $1 is set ?
}

1077
2017-08-30 14:54


起源


if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi。 - Jens
寻求解决方案的注意事项:这个问题有许多高度评价的答案可以回答“变量非空”的问题。 Jens和Lionel在下面的答案中提到了更多的校正解决方案(“变量集”)。 - Nathan Kidd
Russell Harmon和Seamus也是正确的 -v 测试,虽然这似乎只适用于新版本 bash 并且不能通过shell移植。 - Graeme
正如@NathanKidd所指出的,Lionel和Jens给出了正确的解决方案。 prosseek,你应该 切换你接受的答案 其中一个。 - Garrett
......或者错误的答案可能被我们中间更有眼力的人所贬低,因为@prosseek没有解决这个问题。 - dan3


答案:


(通常)正确的方式

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”。

在用户必须指定扩展名或其他属性列表并且未指定它们默认为非空值的情况下,unset和“设置为空字符串”之间的区别是必不可少的,而指定空字符串应该使脚本使用空扩展或其他属性列表。

但是,在每种情况下,这种区别可能并不重要。在那些情况下 [ -z "$var" ] 会没事的。


1608
2017-12-13 17:04



@Garrett,你的编辑让这个答案不正确, ${var+x} 是正确的替代使用。运用 [ -z ${var:+x} ] 没有产生不同的结果 [ -z "$var" ]。 - Graeme
这不起作用。无论var是否设置为值,我都会“未设置”(使用“unset var”清除,“echo $ var”不产生输出)。 - Brent212
对于解决方案的语法$ {parameter + word},官方手册部分是 gnu.org/software/bash/manual/... ;然而,这是一个错误,它并没有非常清楚地提到这种语法,但只是引用(省略冒号[“:”]导致仅对未设置的参数进行测试。$ {parameter:+ word} [意思]如果参数为null或未设置,则不替换任何内容,否则将替换word的扩展。);引用的参考文献 pubs.opengroup.org/onlinepubs/9699919799/utilities/... (相关提示)这里有更清晰的文档。 - Destiny Architect
看起来使用多个表达式时需要引号,例如 [ -z "" -a -z ${var+x} ] 得到 bash: [: argument expected 在bash 4.3-ubuntu中(但不是例如zsh)。我真的不喜欢使用在某些情况下恰好起作用的语法的答案。 - FauxFaux
用一个简单的 [ -z $var ] 没有比省略引号更“错误”。无论哪种方式,你都在假设你的输入。如果您将空字符串视为未设置, [ -z $var ] 是你所需要的全部。 - N13


要检查非null /非零字符串变量,即如果设置,请使用

if [ -n "$1" ]

这与之相反 -z。我发现自己在使用 -n 更多 -z


654
2017-08-30 15:17



我通常喜欢 [[ ]] 过度 [ ]作为 [[ ]] 更强大,在某些情况下引起的问题更少(参见 这个问题 为了解释两者之间的差异)。问题具体要求a 庆典 解决方案并没有提到任何可移植性要求。 - Flow
问题是人们如何看待变量是否存在 组。那你就不能假设变量了 是 组。 - HelloGoodbye
我同意, [] 特别适用于shell(非bash)脚本,效果更好。 - Hengjie
失败了 set -u。 - Ciro Santilli 新疆改造中心 六四事件 法轮功
注意 -n 是默认测试,所以更简单 [ "$1" ] 要么 [[ $1 ]] 也工作。另请注意 [[ ]]  报价是不必要的。 - tne


以下是测试参数是否如何的方法 未设置, 要么 空(“空”) 要么 设置一个值

+--------------------+----------------------+-----------------+-----------------+
|                    |       parameter      |     parameter   |    parameter    |
|                    |   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”显示的所有情况下,都会为参数分配该值,该值也会替换表达式。


390
2018-05-25 20:20



@HelloGoodbye是的确有效: set foo; echo ${1:-set but null or unset} 回声“foo”; set --; echo ${1:-set but null or unset} 回声设置但是空... - Jens
@HelloGoodbye位置参数可以设置,呃, set :-) - Jens
这个答案非常令人困惑。有关如何使用此表的任何实际示例? - Ben Davis
@BenDavis详细信息在POSIX标准的链接中进行了解释,该标准引用了该表的章节。我在几乎所有编写的脚本中使用的一个构造是 : ${FOOBAR:=/etc/foobar.conf} 如果变量未设置或为null,则为其设置默认值。 - Jens
parameter 是任何变量名称。 word 是一些要替换的字符串,具体取决于您使用的表中的语法,以及变量 $parameter 已设置,设置但为null或未设置。不要让事情变得更复杂,但是 word 还可以包含变量!这对于传递由字符分隔的可选args非常有用。例如: ${parameter:+,${parameter}} 输出逗号分隔 ,$parameter 如果设置但不是null。在这种情况下, word 是 ,${parameter} - TrinitronX


有很多方法可以做到这一点,其中之一是:

if [ -z "$1" ]

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


168
2017-08-30 15:00



unset参数和具有null值的参数之间存在差异。 - chepner
我只想迂腐,并指出这是 对面 回答问题所带来的问题。问题是否设置变量IS,而不是设置变量。 - Philluminati
[[ ]] 只不过是一个可移植性的陷阱。 if [ -n "$1" ] 应该在这里使用。 - Jens
这个答案是不正确的。该问题要求一种方法来判断变量是否已设置,而不是设置为非空值。特定 foo="", $foo 有价值,但是 -z 只会报告它是空的。和 -z $foo 如果你有,会爆炸 set -o nounset。 - Keith Thompson
请注意,如果 -u 设置,这将导致错误。 - Daniel C. Sobral


虽然这里陈述的大部分技术都是正确的, bash 4.2 支持对变量存在的实际测试(男人重击),而不是测试变量的值。

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

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

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

146
2017-07-09 02:28



在bash 4.1.2中,无论是否设置了变量, [[ -v aaa ]]; echo $? ==> -bash: conditional binary operator expected  -bash: syntax error near 'aaa' - Dan
在'bash 4.2中添加了'test'内置函数的'-v'参数。 - Ti Strga
-v参数被添加为-u shell选项(nounset)的补充。打开'nounset'(set -u)后,shell将返回错误并终止,如果它不是交互式的。 $#非常适合检查位置参数。但是,除了clobbering之外,命名变量还需要一些其他方法来将它们标识为未设置。遗憾的是,这个功能已经来得太晚了,因为很多版本必然会与早期版本兼容,在这种情况下他们无法使用它。唯一的另一种选择是使用技巧或“黑客”绕过它,如上所示,但它是最不优雅的。 - osirisgothra
我认为osirisgothra关于使用$#检查位置参数的说法应该添加到答案中。不幸的是-v不适用于位置参数(例如[[-v 2]]与[[$#-ge 2]]不一样),至少在我的bash 4.3.11中是这样。 - Jaan
请注意,这不适用于声明但未设置的变量。例如 declare -a foo - miken32


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

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

54
2018-05-08 08:32



我也一直在使用它;只是以减少的方式: [ $isMac ] && param="--enable-shared"
@Bhaskar我认为这是因为 这个答案 已经提供了基本相同的答案(使用 ${variable+x} 要得到 x 当且仅当 $variable 已经差不多半年了。它还有更详细的解释为什么它是正确的。 - Mark Haferkamp


在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

30
2017-08-26 16:24



另请注意 [ -v "$VARNAME" ] 是不正确的,但只是执行不同的测试。假设 VARNAME=foo;然后它检查是否有一个名为的变量 foo 那是设定的。 - chepner
注意那些想要携带, -v 不符合POSIX标准 - kzh
如果有人 [: -v: unexpected operator,一定要确保使用 bash 代替 sh。 - koppor


我总是在里面找到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"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

请注意,每个组(有和没有前面的冒号)都有相同的  和 未设置 情况,所以唯一不同的是如何  案件得到处理。

与前面的冒号,  和 未设置 案件是相同的,所以我会尽可能使用那些(即使用 :=, 不只是 =,因为空案例不一致)。

标题:

  •  手段 VARIABLE 是非空的(VARIABLE="something"
  •  手段 VARIABLE 是空/ null(VARIABLE=""
  • 未设置 手段 VARIABLE 不存在 (unset VARIABLE

价值观:

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

30
2018-06-17 15:19



您只修复了实际编码错误(使用变量时不需要的间接实例)。我做了一个编辑来修复一些案例,其中美元在解释描述时有所作为。顺便说一句,所有shell变量(数组除外)都是字符串变量;可能只是它们包含一个可以解释为数字的字符串。谈论字符串只会使消息变得模糊。行情与字符串无关,它们只是转义的替代方法。 VARIABLE="" 可写成 VARIABLE=。不过,前者更具可读性。 - Palec
啊,这使它更清楚 - 欣赏建议! - deizel


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

18
2017-08-30 15:00



第一次测试是落后的;我想你想要的 [ "$1" != "" ] (要么 [ -n "$1" ])... - Gordon Davisson
@Gordon,修好了! - Paul Creasey
如果这将失败 $1 设置为空字符串。 - HelloGoodbye
答案的第二部分(计数参数)是一个有用的解决方法,答案的第一部分不起作用 $1 设置为空字符串。 - Mark Berry
如果这将失败 set -o nounset 是的。 - Flimm


阅读的“参数扩展”部分 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
}

(注意使用 : 作为null命令,它只是扩展其参数的值。我们不想做任何事情 $1 在这个例子中,如果没有设置则退出)


14
2018-01-31 22:05



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