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

pro*_*eek 1417 variables bash shell

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

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

function a {
    # if $1 is set ?
}
Run Code Online (Sandbox Code Playgroud)

小智 2102

(通常)正确的方式

if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi
Run Code Online (Sandbox Code Playgroud)

这里${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
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 目前还不完全清楚为何需要+ x (35认同)
  • 使用简单的`[-z $ var]`并不比省略引号更"错误".无论哪种方式,您都在假设您的输入.如果您可以将空字符串视为未设置,则可以使用`[-z $ var]`. (34认同)
  • @Garrett,你的编辑使这个答案不正确,`$ {var + x}`是正确的替换使用.使用`[-z $ {var:+ x}]`产生的结果与`[-z"$ var"]`不同. (24认同)
  • 这不起作用.无论var是否设置为值,我都会"未设置"(用"unset var"清除,"echo $ var"不产生输出). (10认同)
  • 对于解决方案的语法$ {parameter + word},官方手册部分是http://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion; 然而,这是一个错误,它并没有非常清楚地提到这种语法,但只是引用(省略冒号[":"]导致仅对未设置的参数进行测试.$ {parameter:+ word} [意思]如果参数为null或未设置,则不替换任何内容,否则将替换word的扩展.); 引用的参考http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02(很高兴知道)这里有更清晰的文档. (9认同)
  • 当你使用多个表达式时看起来像是引号,例如`[ - z"" - a -z $ {var + x}]`在bash 4.3-ubuntu中获取`bash:[:argument expected`(但不是例如zsh) ).我真的不喜欢使用在某些情况下恰好起作用的语法的答案. (7认同)
  • @N13 **!危险!** 您不能省略 `[ -z "$var" ]` 中的引号。比较: `bash -c 'x="a -o -n b"; [ -z "$x" ] && 回显空 || echo set'` (打印 `set`) 与 `bash -c 'x="a -o -n b"; [ -z $x ] && 回显空 || echo set'` (打印 `empty`) 与 `bash -c 'x=$'\n'; [ -z $x ] && 回显空 || echo set'` (打印 `empty`) 与 `bash -c 'x=" -o -n b"; [ -z $x ] && 回显空 || echo set'` (除了 `set` 之外,还向 stderr 打印一些丑陋的错误) (5认同)
  • Bash是最糟糕的 (4认同)
  • 怎么会是安全问题呢?如果脚本不期望从脚本外部获取“var”值,那么它将在开头进行绝对赋值,例如“var=foo”。那么就不可能将值传递到脚本中。 (3认同)
  • 代码:[ - z $ {var + x}]在"x"上进行shell扩展.如果IFS恰好是x(IFS = x),则测试失败.简单引用:`[ - z"$ {var + x}"]`解决问题. (3认同)
  • [ -z $var ] 错误的原因是它没有提供“是否设置了变量”的测试。它提供了“变量是否已设置且非空”的测试。在 shell 编程中,将两者视为等效是很常见的,但这仍然是一个不同的测试。至于引号:通常最好对每个参数扩展进行引号,除非您**知道**您想要分词和文件通配。这些功能就像一个陷阱,一旦遇到极端情况就会破坏您的脚本。但如前所述,在这种情况下引用并不重要。 (3认同)
  • @GordonDavisson `-z` 的相反是 `-n`,所以它是 `[ -n "${foo+x}" ]` 来测试“设置,可能为空”。请注意,我建议使用引号,因为这样可以更轻松地添加更多测试,例如 `[ -n "${foo+x}" -a -n "${baz+x}" ]` (也可以是写为 `[ -n "${foo+${baz+x}}" ]`)。请注意, `[ -n "${foo+x}" -o -n "${baz+x}" ]` 可以缩写为 `[ -n "${foo+x}${baz+x}" ]`。 (3认同)
  • @Fuseteam,`[ -z ${var+x} ]`的引用是错误的(`[ -z ]`被视为`[ -n -z ]`,这是一个测试字符串`-z ` 是非空的,不是测试 `${var+x}` 是否扩展到任何特定值)。它应该是 `[ -z "${var+x}" ]`,对于设置但为空的变量返回 true,对于设置且非空的变量返回 true,对于未设置的变量返回 false。即使两个测试的结果相同,使用正确的语法也可以使该机制更容易推理。 (3认同)
  • @Tino 虽然显然 -n 与 -z 相反,但它的行为并不相似。虽然 `-z` 和 `-z ""` 自然会呈现相同的结果,但 `-n` 和 `-n ""` 不会。`-n` 始终测试 true,而 `-n ""` 始终测试 false。这使得 -nie `[ -n "${foo+x}" ]` 所需的引号应始终与引号一起使用。为了安全起见,也不用费力去记忆,我总是会使用引号。 (3认同)
  • 请注意这一点。如果我有一个带有以下答案的脚本(tmp.sh):`if [ -z ${var+x} ]; 然后 echo "var 未设置"; else echo "var 设置为 '$var'"; fi` 并将其运行为 `var=sneaky ./tmp.sh` var 将在脚本中设置为 Sneaky。这是一个很好的选择,但在使用这种类型的参数扩展表示法时,它确实会带来可能的安全问题。 (2认同)
  • 如何为参数做到这一点。1 美元 (2认同)
  • `` { [ -z ${var+x} ] && echo "无值" ; } || echo " set" ksh: test: 0403-004 使用此命令指定参数。set`` 这不适用于单个方括号(旧的 posix 版本)。它将以这种方式工作 ` { [[ -z ${var+x} ]] && echo "no val" ; } || echo "set" ` 这是旧的 '[' 测试的现代版本 (2认同)
  • 具体来说,关于这个答案,使用 `[[` & `]]` 还是 `[` & `]` 更好? (2认同)
  • 根据提供的文档,确实没有正确或错误的方法来做到这一点。这仅取决于您在给定上下文中的意图。有趣的是@jdf 发现了 TOCTOU 事件的竞争条件。我认为对于大多数 shell 脚本来说都是如此。不一定只是在这种情况下。 (2认同)
  • @ArnoldRoa 检查参数只需使用“1”代替“var”。所以... `[ -z ${1+x} ]` (2认同)
  • 注意:这与`set -o nounset`又名`set -u`是兼容的,你应该和`set -o errexit`又是`set -e`或者'set -eu`一起使用. (2认同)
  • @chrisinmtown 这会将 set-to-"" 与 unset 混淆,并且如果变量包含空格或 shell 通配符,也可能会奇怪地失败。shell 脚本中不带引号的变量引用几乎总是一个错误。 (2认同)
  • 是否有一种清晰的方式来颠覆这种逻辑?条件检查逆. (2认同)
  • 为什么`-z`?不仅仅是`[$ {var + x}]`可以回答问题吗? (2认同)
  • 一个巧妙(但可能“太可爱”)的技巧是使用替换字符串来形成一个真/假结果:`if ${var+false} true; 然后 echo "var 未设置"; else echo "var 设置为 '$var'"; fi`(在扩展中绝对没有引号),因此 `true` 要么是一个命令,要么是 `false` 命令的(被忽略的)参数。 (2认同)
  • 这是最好的POSIX sh答案。但是它对于未设置位置0的数组(至少从3.0开始为Bash功能)失败。例如,`var =(); 如果[-z $ {var + x}]; 然后回显“未设置”;否则回显“设置”;fi会回显unset,而define -p var返回0并输出declare -a var =()。我在下面的答案中有完整的解释。 (2认同)
  • 在 unix shell 中“区分未设置的变量和设置为空字符串的变量”的建议是一个糟糕的建议。现实世界中没有人会暗示这一点,因为在 shell 中这样做并不常见。所以我会很容易地执行“SOMEVAR=”your_script.sh`来取消当前环境中的某些变量,然后就被搞砸了。如果你想遵循 POLA,你绝对不能在 shell 中区分 unset 和空变量。 (2认同)

小智 832

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

if [ -n "$1" ]
Run Code Online (Sandbox Code Playgroud)

这与之相反-z.我发现自己使用的-n不仅仅是-z.

您会像以下一样使用它:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi
Run Code Online (Sandbox Code Playgroud)

  • 我通常更喜欢`[[]]`而不是`[]`,因为`[[]]`更强大并且在某些情况下引起的问题更少(参见[this question](http://stackoverflow.com/questions/3427872)为了解释两者之间的差异,我们之间的区别是什么?该问题特别要求*bash*解决方案,并未提及任何可移植性要求. (80认同)
  • 在`set -u`上失败. (67认同)
  • 注意`-n`是默认测试,所以更简单的`["$ 1"]`或`[[$ 1]]`也可以.另请注意,使用`[[]]`[引用是不必要的](http://wiki.bash-hackers.org/syntax/ccmd/conditional_expression#word_splitting). (58认同)
  • 问题是人们如何看到变量是否为*set*.那么你不能假设变量*是*set. (17认同)
  • 我同意,`[]`特别适用于shell(非bash)脚本. (6认同)
  • 在 CentOS undet tcsh 上,`if [ -n "$BASH" ] ; 然后回声 y; 否则回声 n; fi` 说“BASH:未定义的变量” (2认同)
  • 似乎它不是真的相反......真正的相反是`if [!-z "$1" ]` (2认同)
  • 最好正式测试变量是否存在,而不是根据其值进行测试。请参阅拉塞尔·哈蒙的回答。 (2认同)
  • 请注意,如果您使用“bash -u ...”或“set -o nounset”,这将不起作用(如前面所说)。在这种情况下,请使用“if [[ -n ${VAR-} ]]”。如果未设置“$VAR”,则会将其设置为空字符串,并且“-n”将按预期工作 (2认同)

Jen*_*ens 443

以下是如何测试参数是否未设置,或者为空("Null")使用值设置:

+--------------------+----------------------+-----------------+-----------------+
|                    |       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 |
+--------------------+----------------------+-----------------+-----------------+
Run Code Online (Sandbox Code Playgroud)

来源:POSIX:参数扩展:

在使用"替换"显示的所有情况下,表达式将替换为显示的值.在使用"assign"显示的所有情况下,都会为参数分配该值,该值也会替换表达式.

  • 这个答案非常令人困惑.有关如何使用此表的任何实际示例? (82认同)
  • @BenDavis详细信息在POSIX标准的链接中进行了解释,该标准引用了该表的章节.我在几乎所有编写的脚本中都使用的一个构造是`:$ {FOOBAR:=/etc/foobar.conf}`来设置变量的默认值,如果它未设置或为null. (10认同)
  • @HelloGoodbye是的确有效:`set foo; echo $ {1:-set but null或unset}`echos"foo"; `集 - ; echo $ {1:-set但null或unset}`回声设置但是null ... (5认同)
  • @HelloGoodbye位置参数可以设置为,呃,`set` :-) (4认同)
  • 干得好,延斯。我一直喜欢 POSIX 版本的答案,尽管 OP 已经将其限制为 Bash。 (3认同)
  • `parameter` 是任何变量名。`word` 是一些要替换的字符串,具体取决于您在表中使用的语法,以及变量 `$parameter` 是已设置、已设置但为空还是未设置。不是为了让事情变得更复杂,但 `word` 也可以包含变量!这对于传递由字符分隔的可选参数非常有用。例如:`${parameter:+,${parameter}}` 输出逗号分隔的 `,$parameter` 如果它被设置但不为空。在这种情况下,`word` 是 `,${parameter}` (2认同)

小智 225

虽然这里陈述的大部分技术都是正确的,但是bash 4.2支持对变量(man bash)的存在进行实际测试,而不是测试变量的值.

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

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

foo=""
[[ -v foo ]]; echo $?
# 0
Run Code Online (Sandbox Code Playgroud)

值得注意的是,这种方法在用于检查set -u/ set -o nounsetmode中的未设置变量时不会导致错误,这与许多其他方法(例如使用)不同[ -z.

  • 'test'内置函数的'-v'参数在bash 4.2中添加. (30认同)
  • -v参数被添加为-u shell选项(nounset)的补充.打开'nounset'(set -u)后,shell将返回错误并终止,如果它不是交互式的.$#非常适合检查位置参数.但是,除了clobbering之外,命名变量还需要一些其他方法来将它们识别为未设置.不幸的是,这个功能已经来得太晚了,因为很多版本必然会与早期版本兼容,在这种情况下他们无法使用它.唯一的另一种选择是使用技巧或"黑客"绕过它,如上所示,但它是最不优雅的. (19认同)
  • 这就是为什么我在接受/最高投票的答案之后继续阅读. (12认同)
  • 在bash 4.1.2中,无论是否设置变量,`[[-v aaa]]; echo $?`==>`-bash:条件二元运算符expected`` -bash:'aaa'附近的语法错误 (9认同)
  • 我认为 osirisgothra 关于使用 $# 检查位置参数的评论应该添加到答案中。不幸的是 -v 不适用于位置参数(例如 [[ -v 2 ]] 与 [[ $# -ge 2 ]] 不同),至少在我的 bash 4.3.11 中是这样。 (2认同)
  • 注意,这不适用于已声明但未设置的变量。例如`declare -a foo` (2认同)
  • 当变量的名称在另一个变量中并且您想使用[bash“严格模式”](http://redsymbol.net/articles/unofficial-bash-strict-mode/)时,这是首选解决方案。所以 `[[ -v "${VARNAME:-}" ]]` 将在所有 3 种情况下正确运行:1) VARNAME 未设置,2) 它被设置为未设置变量的名称,以及 3) 它被设置为名称的一个集合变量。 (2认同)
  • 这是标量的正确方法,并且在 ksh 中也可用。请注意:如果您不想浪费半个小时盯着屏幕想知道发生了什么,那么它是“[[ -v foo ]]”而不是“[[ -v $foo ]]”。除了验证它是否已设置之外,您通常还会寻找特定值,例如“123”。在这种情况下,我更喜欢使用 `[[ ${foo-123} -eq 123 ]]` 来压缩 `[[ -v foo && $foo -eq 123 ]]` (2认同)
  • 有没有办法将“-v”条件表达式与位置参数“$1”、“$2”等一起使用?我似乎无法让它与 Bash 5.0 一起工作。即“[[ -v 1 ]]”总是返回1,即使设置了“$1”。将这些变量预先分配给临时变量(例如“PARAM1=$1; [[ -v PARAM1 ]]”)也不起作用,因为即使这些变量的内容为空,它们也会被设置。 (2认同)

enn*_*ler 175

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

if [ -z "$1" ]
Run Code Online (Sandbox Code Playgroud)

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

  • 这个答案是不正确的.该问题询问是否设置变量的方法,而不是设置为非空值.给定`foo =""`,`$ foo`有一个值,但`-z`只会报告它是空的.如果你有`set -o nounset`,`-z $ foo`会爆炸. (71认同)
  • `[[]]`只不过是一个可移植性的陷阱.`if if [-n"$ 1"]`应该在这里使用. (46认同)
  • unset参数和具有null值的参数之间存在差异. (43认同)
  • 我只是想迂腐,并指出这是问题所带来的*相反的答案.问题是否设置变量IS,而不是设置变量. (21认同)
  • 请注意,如果设置了`-u`,则会导致错误. (19认同)
  • 它取决于"变量设置"的定义.如果要检查变量是否设置为**非零**字符串,则[mbranning answer](http://stackoverflow.com/a/3601734/194894)是正确的.但是如果你想检查变量是否已声明但未初始化(即`foo =`),那么[Russel的答案](http://stackoverflow.com/a/17538964/194894)是正确的. (4认同)
  • 所以,当然,这是正确答案? (2认同)
  • 这不会在`set -u`下失败吗? (2认同)

dei*_*zel 66

我总是发现另一个答案中的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"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
Run Code Online (Sandbox Code Playgroud)

请注意,每个组(有和没有前置冒号)具有相同的setunset情况,因此唯一不同的是如何处理案例.

对于前面的冒号,和未设置的情况是相同的,所以我会尽可能使用那些(即使用:=,不仅仅是=因为空的情况不一致).

标题:

  • set means VARIABLE是非空的(VARIABLE="something")
  • 空的意思VARIABLE是空/ null(VARIABLE="")
  • unset手段VARIABLE不存在(unset VARIABLE)

价值观:

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


小智 64

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

if [[ $var ]]; then ...       # `$var' expands to a nonempty string
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

相反的测试是否未设置变量:

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


Sea*_*nor 33

在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
Run Code Online (Sandbox Code Playgroud)

  • 另请注意,`[ - v"$ VARNAME"]`不正确,只是执行不同的测试.假设`VARNAME = foo`; 然后它检查是否有一个名为`foo`的变量被设置. (5认同)
  • 注意那些想要移植性的东西,`-v`不符合POSIX标准 (4认同)

Pau*_*sey 21

if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi
Run Code Online (Sandbox Code Playgroud)

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

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi
Run Code Online (Sandbox Code Playgroud)

  • 如果将'$ 1`设置为空字符串,则会失败. (10认同)
  • 答案的第二部分(计数参数)是一个有用的解决方法,因为当$ 1设置为空字符串时,答案的第一部分不起作用。 (2认同)

ece*_*ulm 18

对于那些希望在脚本中 检查未设置或空的人set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi
Run Code Online (Sandbox Code Playgroud)

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

  • `$ {var: - }`和`$ {var-}`意味着不同的东西.如果`var` unset**或null**,`$ {var: - }`将扩展为空字符串,而'$ {var-}`只有在未设置时才会扩展为空字符串. (3认同)

Sho*_*dan 17

如果你像我一样,你正在寻找的实际上是

“bash 仅在设置变量时运行命令”

你希望将其作为一句台词,所以下面这行就是你想要的

仅适用于 Bash 4.2 或更高版本

仅在设置时运行

if [[ -v mytest ]]; then echo "this runs only if variable is set"; fi
Run Code Online (Sandbox Code Playgroud)

仅在未设置时运行

if [[ ! -v mytest2 ]]; then echo "this runs only if variable is not set"; fi
Run Code Online (Sandbox Code Playgroud)


Jar*_*ney 16

如果未设置,您想要退出

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

#!/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}"
Run Code Online (Sandbox Code Playgroud)

运行时返回错误

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument
Run Code Online (Sandbox Code Playgroud)

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

如果您只想检查值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
Run Code Online (Sandbox Code Playgroud)

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

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"
Run Code Online (Sandbox Code Playgroud)

仅检查,无退出 - 仅空为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
Run Code Online (Sandbox Code Playgroud)

简短的测试

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"
Run Code Online (Sandbox Code Playgroud)

我把这个答案献给了@ mklement0(评论),他们挑战我准确地回答这个问题.

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


Gil*_*il' 15

要检查变量是否设置为非空值,请使用[ -n "$x" ],如其他人已经指出的那样.

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

要检查参数是否已通过,test $#,这是传递给函数的参数数量(或者不在函数中时传递给脚本的参数)(参见Paul的答案).


che*_*ner 14

阅读bash手册页的"参数扩展"部分.参数扩展不为要设置的变量提供常规测试,但如果未设置参数,则可以对参数执行多项操作.

例如:

function a {
    first_arg=${1-foo}
    # rest of the function
}
Run Code Online (Sandbox Code Playgroud)

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

function a {
    : ${1?a must take a single argument}
    # rest of the function
}
Run Code Online (Sandbox Code Playgroud)

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


Mar*_*amp 14

注意

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

简短答案

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

is-variable-set() {
    declare -p $1 &>dev/null
}
Run Code Online (Sandbox Code Playgroud)

为什么这样做

在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
}
Run Code Online (Sandbox Code Playgroud)

测试用例代码

请注意,如果未将变量声明为关联数组,则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
Run Code Online (Sandbox Code Playgroud)

测试输出

该测试在助记符标题行对应于[ -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
Run Code Online (Sandbox Code Playgroud)

摘要

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

  • 在我尝试过的答案中,这个(`declare -p var&> / dev / null`)是唯一有效的答案。谢谢! (3认同)
  • @mark-haferkamp我尝试编辑你的答案,将前面的关键“/”添加到你的函数代码中,这样它就会读取`... &> /dev/null`,但这样不会让我进行单个字符编辑(必须是> 6 个字符 - 甚至尝试添加空格,但它不起作用)。另外,可能值得更改函数以将示例命名变量(例如“OUTPUT_DIR”)切换为“$1”,因为正如您所指出的,像“$1”这样的特殊变量在这里不起作用。无论如何,这是一个关键的编辑,否则代码将在名为“dev”的相对目录中创建一个名为“null”的文件。顺便说一句 - 很好的答案! (2认同)

Ale*_*oVD 13

在bash中你可以-v[[ ]]内置中使用:

#! /bin/bash -u

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

echo $SOMEVAR
Run Code Online (Sandbox Code Playgroud)


Jea*_*vin 12

要测试变量是否var已设置:[ ${var+x} ].

\n

要测试变量是否按名称设置:[ ${!name+x} ]

\n

要测试是否设置了位置参数:[ ${N+x} ],其中 N 实际上是一个整数。

\n

这个答案几乎与 Lionel\xe2\x80\x99s 类似,但通过省略来探索更简约的方式-z.

\n

要测试是否设置了命名变量:

\n
function is_set {\n    local v=$1\n    echo -n "${v}"\n    if [ ${!v+x} ]; then\n        echo " = \'${!v}\'"\n    else\n        echo " is unset"\n    fi\n}\n
Run Code Online (Sandbox Code Playgroud)\n

要测试是否设置了位置参数:

\n
function a {\n    if [ ${1+x} ]; then\n        local arg=$1\n        echo "a \'${arg}\'"\n    else\n        echo "a: arg is unset"\n    fi\n}\n
Run Code Online (Sandbox Code Playgroud)\n

测试表明不需要额外注意空格和有效的测试表达式。

\n
set -eu\n\nV1=a\nV2=\nV4=-gt\nV5="1 -gt 2"\nV6="! -z 1"\nV7=\'$(exit 1)\'\n\nis_set V1\nis_set V2\nis_set V3\nis_set V4\nis_set V5\nis_set V6\nis_set V7\n\na 1\na\na "1 -gt 2"\na 1 -gt 2\n
Run Code Online (Sandbox Code Playgroud)\n
$ ./test.sh\n\nV1 = \'a\'\nV2 = \'\'\nV3 is unset\nV4 = \'-gt\'\nV5 = \'1 -gt 2\'\nV6 = \'! -z 1\'\nV7 = \'$(exit 1)\'\na \'1\'\na: arg is unset\na \'1 -gt 2\'\na \'1\'\n
Run Code Online (Sandbox Code Playgroud)\n

最后,请注意它set -eu可以保护我们免受常见错误的影响,例如变量名称中的拼写错误。我建议使用它,但这意味着未设置的变量和设置为空字符串的变量之间的差异得到了正确处理。

\n


inf*_*ler 11

概括

  • 使用test -n "${var-}"以检查变量不为空(并因此必须定义/设置得)。用法:

    if test -n "${name-}"; then
      echo "name is set to $name"
    else
      echo "name is not set or empty"
    fi
    
    Run Code Online (Sandbox Code Playgroud)
  • 使用test ! -z "${var+}"以检查变量定义/套(即使是空的)。用法:

    if test ! -z "${var+}"; then
      echo "name is set to $name"
    else
      echo "name is not set"
    fi
    
    
    Run Code Online (Sandbox Code Playgroud)

请注意,第一个用例在 shell 脚本中更为常见,这也是您通常想要使用的。

笔记

  • 此解决方案应该适用于所有 POSIX shell(sh、bash、zsh、ksh、dash)
  • 这个问题的一些其他答案是正确的,但可能会让没有 shell 脚本经验的人感到困惑,所以我想提供一个 TLDR 答案,对这些人来说最不容易混淆。

解释

要了解此解决方案的工作原理,您需要了解POSIXtest命令和 POSIX shell 参数扩展 ( spec ),因此让我们介绍了解答案所需的绝对基础知识。

test 命令评估表达式并返回 true 或 false(通过其退出状态)。-n如果操作数是非空字符串,则运算符返回 true。例如,test -n "a"返回true,而test -n ""返回false。现在,要检查变量是否为空(这意味着它必须被定义),您可以使用test -n "$var". 但是,一些 shell 脚本有一个选项 set ( set -u),它会导致对未定义变量的任何引用发出错误,因此如果未定义变量var,则表达式$a将导致错误。要正确处理这种情况,您必须使用变量扩展,如果变量未定义,它将告诉 shell 用替代字符串替换变量,避免上述错误。

变量扩展${var-}意味着:如果变量var未定义(也称为“未设置”),则将其替换为空字符串。因此,test -n "${var-}"如果$var不为空,则返回 true ,这几乎总是您要在 shell 脚本中检查的内容。反向检查,如果$var未定义或不为空,则为test -z "${var-}".

现在到第二个用例:检查变量var是否已定义,是否为空。这是一个不太常见的用例,稍微复杂一些,我建议您阅读Lionels 的精彩答案以更好地理解它。


Cam*_*son 9

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

if test "${name+x}"; then
    echo 'name is set'
else
    echo 'name is not set'
fi
Run Code Online (Sandbox Code Playgroud)

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

以下是我想出的一些明确的表达方式:

+-----------------------+-------------+---------+------------+
| 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       |
+-----------------------+-------------+---------+------------+
Run Code Online (Sandbox Code Playgroud)

顺便说一下,这些表达式是等价的: test <expression><=>[ <expression> ]

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

+----------------------+-------------+---------+------------+
| 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       |
+----------------------+-------------+---------+------------+
Run Code Online (Sandbox Code Playgroud)


Per*_*-lk 6

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

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset
Run Code Online (Sandbox Code Playgroud)

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

对于环境变量:

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

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

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"
Run Code Online (Sandbox Code Playgroud)

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

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"
Run Code Online (Sandbox Code Playgroud)

笔记:

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


cod*_*ict 5

你可以做:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}
Run Code Online (Sandbox Code Playgroud)

  • 或者你可以做`-n`而不是否定`-z`。 (9认同)
  • 如果`$1` 设置为空字符串,这将失败。 (5认同)

kev*_*rpe 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.
}
Run Code Online (Sandbox Code Playgroud)

相关:在Bash中,如何测试是否在“ -u”模式下定义了变量


Mik*_*ark 5

我很惊讶没有人试图编写一个 shell 脚本来以编程方式生成臭名昭著的难以理解的 table。既然我们在这里学习编码技术,为什么不用代码表达答案呢?:) 这是我的看法(应该适用于任何 POSIX shell):

H="+-%s-+-%s----+-%s----+-%s--+\n"       # table divider printf format
R="| %-10s | %-10s | %-10s | %-10s |\n"  # table row printf format

S='V'     # S is a variable that is set-and-not-null
N=''      # N is a variable that is set-but-null (empty "")
unset U   # U is a variable that is unset

printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "expression" "FOO='V'" "FOO='' " "unset FOO";
printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "\${FOO:-x}" "${S:-x}" "${N:-x}" "${U:-x}  "; S='V';N='';unset U
printf "$R" "\${FOO-x} " "${S-x} " "${N-x} " "${U-x}   "; S='V';N='';unset U
printf "$R" "\${FOO:=x}" "${S:=x}" "${N:=x}" "${U:=x}  "; S='V';N='';unset U
printf "$R" "\${FOO=x} " "${S=x} " "${N=x} " "${U=x}   "; S='V';N='';unset U
printf "$R" "\${FOO:?x}" "${S:?x}" "<error>" "<error>  "; S='V';N='';unset U
printf "$R" "\${FOO?x} " "${S?x} " "${N?x} " "<error>  "; S='V';N='';unset U
printf "$R" "\${FOO:+x}" "${S:+x}" "${N:+x}" "${U:+x}  "; S='V';N='';unset U
printf "$R" "\${FOO+x} " "${S+x} " "${N+x} " "${U+x}   "; S='V';N='';unset U
printf "$H" "----------" "-------" "-------" "---------";

# For reference, the following two lines are the literal rows of the table
# which would cause the script to exit and so have had <error> written in.
#printf "$R" "\${FOO:?x}" "${S:?x}" "${N:?x}" "${U:?x}  "; S='V';N='';unset U
#printf "$R" "\${FOO?x} " "${S?x} " "${N?x} " "${U?x}   "; S='V';N='';unset U
Run Code Online (Sandbox Code Playgroud)

以及运行脚本的输出:

+------------+------------+------------+------------+
| expression | FOO='V'    | FOO=''     | unset FOO  |
+------------+------------+------------+------------+
| ${FOO:-x}  | V          | x          | x          |
| ${FOO-x}   | V          |            | x          |
| ${FOO:=x}  | V          | x          | x          |
| ${FOO=x}   | V          |            | x          |
| ${FOO:?x}  | V          | <error>    | <error>    |
| ${FOO?x}   | V          |            | <error>    |
| ${FOO:+x}  | x          |            |            |
| ${FOO+x}   | x          | x          |            |
+------------+------------+------------+------------+
Run Code Online (Sandbox Code Playgroud)

该脚本缺少一些功能,例如在副作用分配发生(或不发生)发生时显示,但也许其他一些更有野心的人想要从这个起点开始运行。

  • 干得好,迈克。看起来不错。正如您所说,它并不完全全面,但可以很快找到并进行比较。 (2认同)