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

How to check if a variable is set in Bash

提问人:prosseek 提问时间:8/30/2010 最后编辑:Peter Mortensenprosseek 更新时间:5/1/2023 访问量:2221214

问:

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

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

function a {
    # if $1 is set ?
}
bash shell 变量

评论

17赞 Jens 7/10/2013
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
285赞 Nathan Kidd 11/30/2013
寻求解决方案的人请注意:这个问题有许多高度评价的答案,它们回答了“变量为非空”的问题。下面的 Jens 和 Lionel 的回答中提到了更多的校正解决方案(“是变量集”)。
12赞 Graeme 1/29/2014
Russell Harmon 和 Seamus 的测试也是正确的,尽管这似乎只适用于新版本的外壳,而不能跨 shell 移植。-vbash
6赞 Garrett 2/14/2014
正如 @NathanKidd 所指出的,Lionel 和 Jens 给出了正确的解决方案。prosseek,您应该将您接受的答案切换到其中之一。
4赞 dan3 7/15/2014
...或者不正确的答案可能会被我们中间更有眼光的人否决,因为@prosseek没有解决问题。

答:

44赞 Paul Creasey 8/30/2010 #1
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赞 Gordon Davisson 8/31/2010
第一个测试是向后测试;我想你想要(或)......[ "$1" != "" ][ -n "$1" ]
12赞 HelloGoodbye 11/28/2013
如果设置为空字符串,这将失败。$1
2赞 Mark Berry 7/30/2014
答案的第二部分(计数参数)是答案的第一部分在设置为空字符串时不起作用的有用解决方法。$1
0赞 Flimm 5/15/2015
如果打开,这将失败。set -o nounset
0赞 gaoithe 4/12/2023
我疯了吗?使用 args 从 bash 获取脚本不会设置 FOO。 工作正常。bash 4.2.46(2) 版本(x86_64-redhat-linux-gnu)[[ -n $1 ]] && FOO=$1[[ "$1" != "" ]] && FOO=$1
216赞 ennuikiller 8/30/2010 #2

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

if [ -z "$1" ]

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

评论

58赞 chepner 3/12/2013
未设置的参数和具有 null 值的参数之间存在差异。
29赞 Philluminati 6/3/2013
我只是想迂腐一点,并指出这与问题提出的答案相反。这些问题询问变量是否设置,而不是变量是否未设置。
50赞 Jens 7/10/2013
[[ ]]只不过是一个可移植性的陷阱。 应该在这里使用。if [ -n "$1" ]
78赞 Keith Thompson 8/6/2013
这个答案是不正确的。该问题要求提供一种方法来判断变量是否已设置,而不是是否设置为非空值。给定 ,有一个值,但只会报告它是空的。如果你有,就会爆炸.foo=""$foo-z-z $fooset -o nounset
4赞 Flow 10/13/2013
这取决于“变量已设置”的定义。如果要检查变量是否设置为非零字符串,则 mbranning 答案是正确的。但是如果你想检查变量是否被声明但未初始化(即),那么罗素的答案是正确的。foo=
10赞 codaddict 8/30/2010 #3

您可以执行以下操作:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}

评论

11赞 Dennis Williamson 8/31/2010
或者你可以做而不是否定 .-n-z
1赞 Gilles 'SO- stop being evil' 8/31/2010
您需要引号,即 .或者你可以用 bash/ksh/zsh 编写。$1[ ! -z "$1" ][[ ! -z $1 ]]
5赞 HelloGoodbye 11/28/2013
如果设置为空字符串,这将失败。$1
1331赞 mbrannig 8/30/2010 #4

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

if [ -n "$1" ]

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

您可以像这样使用它:

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

评论

114赞 Flow 10/13/2013
我通常更喜欢 ,因为它更强大,在某些情况下引起的问题更少(有关两者之间差异的解释,请参阅此问题)。该问题专门要求提供 bash 解决方案,并且没有提到任何可移植性要求。[[ ]][ ][[ ]]
26赞 HelloGoodbye 11/12/2013
问题是如何查看是否设置了变量。那么你就不能假设变量设置的。
10赞 Hengjie 6/2/2014
我同意,效果更好,尤其是对于 shell(非 bash)脚本。[]
85赞 tne 11/26/2014
请注意,这是默认测试,因此更简单或也可以工作。另请注意,引用是不必要的-n[ "$1" ][[ $1 ]][[ ]]
7赞 The Godfather 8/17/2018
似乎并不是真的相反......真正的相反是if [ ! -z "$1" ]
-4赞 ghostdog74 8/30/2010 #5
case "$1" in
 "") echo "blank";;
 *) echo "set"
esac

评论

4赞 HelloGoodbye 11/28/2013
如果设置为空字符串,这将失败。$1
18赞 Gilles 'SO- stop being evil' 8/31/2010 #6

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

大多数情况下,最好将具有空值的变量与未设置的变量相同。但是,如果需要,您可以区分两者:( 扩展为 if is set 和空字符串 if is unset)。[ -n "${x+set}" ]"${x+set}"setxx

要检查参数是否已传递,请 test ,这是传递给函数(或脚本,当不在函数中时)的参数数(参见 Paul 的回答)。$#

19赞 chepner 2/1/2012 #7

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

例如:

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

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

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

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

评论

1赞 tripleee 9/12/2015
令我感到困惑的是,其他答案都没有提到简单而优雅。: ${var?message}
-3赞 koola 11/21/2012 #8

要检查是否设置了变量:

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

评论

6赞 HelloGoodbye 11/28/2013
如果设置为空字符串,这将失败。$var
3301赞 Lionel 12/14/2012 #9

(通常)正确的方式

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

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

引言题外话

引号可以省略(所以我们可以说代替),因为这种语法和用法保证它只会扩展到不需要引号的东西(因为它要么扩展到(不包含分词符,所以它不需要引号),要么什么都不用(这导致 ,它方便地计算为相同的值(true))。${var+x}"${var+x}"x[ -z ][ -z "" ]

然而,虽然引号可以安全地省略,而且对所有人来说都不是很明显的(对于这个引号解释的第一作者来说,他也是一个主要的 Bash 编码员甚至都不明显),但有时最好用引号写成 ,以非常小的 O(1) 速度损失的代价。第一作者还将其添加为使用此解决方案的代码旁边的注释,该解决方案提供了此答案的 URL,现在还包括对为什么可以安全地省略引号的解释。[ -z "${var+x}" ]

(经常)错误的方式

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

这通常是错误的,因为它不区分未设置的变量和设置为空字符串的变量。也就是说,如果 ,那么上面的解将输出“var is blank”。var=''

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

不过,这种区别可能并非在所有情况下都是必不可少的。在这些情况下会很好。[ -z "$var" ]

评论

53赞 Graeme 2/14/2014
@Garrett,您的编辑使此答案不正确,是要使用的正确替代。使用产生的结果与 没有不同的结果。${var+x}[ -z ${var:+x} ][ -z "$var" ]
18赞 Brent212 9/10/2014
这是行不通的。无论 var 是否设置为值,我都会得到“未设置”(使用“unset var”清除,“echo $var”不产生输出)。
18赞 Destiny Architect 11/8/2014
对于解决方案的语法 ${parameter+word},官方手册部分为 gnu.org/software/bash/manual/... ;然而,其中有一个错误,它没有非常清楚地提到这个语法,而是说只是quote(省略冒号[ “:”] 只会导致对未设置的参数进行测试。${parameter:+word} [means] 如果参数为 null 或未设置,则不替换任何内容,否则替换单词的扩展。引用的参考文献 pubs.opengroup.org/onlinepubs/9699919799/utilities/... (很高兴知道)这里有更清晰的文档。
11赞 FauxFaux 6/11/2015
当您使用多个表达式时,似乎需要引号,例如 在 Bash 4.3-Ubuntu 中获取(但不是例如 zsh)。我真的不喜欢使用在某些情况下碰巧有效的语法来回答。[ -z "" -a -z ${var+x} ]bash: [: argument expected
73赞 nshew13 5/6/2016
使用简单并不比省略引号更“错误”。无论哪种方式,您都是在对输入做出假设。如果您可以将空字符串视为未设置,那么您只需要这样做。[ -z $var ][ -z $var ]
76赞 phkoester 5/8/2013 #10

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

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

评论

0赞 11/30/2014
我也已经使用它一段时间了;只是以一种简化的方式:[ $isMac ] && param="--enable-shared"
0赞 Mark Haferkamp 2/16/2018
@Bhaskar我认为这是因为这个答案在将近半年前就已经提供了基本相同的答案(用于获取 iff 已设置)。此外,它还有更多细节解释了为什么它是正确的。${variable+x}x$variable
-1赞 user1857592 5/26/2013 #11

如果你想测试一个变量是绑定的还是未绑定的,这很有效,即使你打开了名词集选项:

set -o noun set

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

评论

1赞 Keith Thompson 8/6/2013
我想你的意思是,不是.这仅适用于已编辑的变量。它还会以一种难以撤消的方式更改您的设置。set -o nounsetset -o noun setexport
0赞 usretc 7/19/2021
如果一个人真的想采用这种方法,无论出于何种原因,子壳就是方法:.或者更简单:if (set -u; : $var) 2>/dev/nullif (: ${var?}) 2>/dev/null
774赞 Jens 5/26/2013 #12

下面介绍如何测试参数是否未设置为空(“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:参数扩展:

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

要在实际操作中显示这一点,请执行以下操作:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  When FOO="world"    |  When 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           | ""              |
+--------------------+----------------------+-----------------+-----------------+

评论

8赞 Jens 11/27/2013
@HelloGoodbye 是的,它确实有效:回声“foo”; 回声设置但为空 ...set foo; echo ${1:-set but null or unset}set --; echo ${1:-set but null or unset}
6赞 Jens 11/28/2013
@HelloGoodbye位置参数可以用,呃,:-)set
156赞 Ben Davis 9/28/2014
这个答案非常令人困惑。关于如何使用这张表,有什么实际的例子吗?
16赞 Jens 3/10/2015
@BenDavis 详细信息在 POSIX 标准的链接中进行了解释,该标准引用了该表所摘自的章节。在我编写的几乎任何脚本中,我使用的一种结构是,如果变量未设置或为 null,则为变量设置默认值。: ${FOOBAR:=/etc/foobar.conf}
3赞 TrinitronX 1/20/2016
parameter是任何变量名称。 是要替换的某个字符串,具体取决于您使用的表中的语法,以及变量是已设置、已设置但为 null 还是未设置。不要让事情变得更复杂,但也可以包括变量!这对于传递由字符分隔的可选参数非常有用。例如:如果设置了逗号,则输出逗号分隔,但未为 null。在本例中,是word$parameterword${parameter:+,${parameter}},$parameterword,${parameter}
-1赞 solidsnack 6/30/2013 #13
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

评论

0赞 11/18/2013
$1,并且 up to 始终处于设置状态。对不起,这是失败的。$2$N
0赞 11/21/2013
我试过了,但它没有用,也许我的 bash 版本缺乏对此的支持。Idk,对不起,如果我错了。:)
461赞 eatnumber1 7/9/2013 #14

虽然这里提到的大多数技术都是正确的,但 Bash 4.2 支持对变量是否存在进行实际测试 (man bash),而不是测试变量的值。

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

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

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

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

评论

14赞 Dan 10/31/2013
在 bash 4.1.2 中,无论是否设置了变量,[[ -v aaa ]]; echo $? ==> -bash: conditional binary operator expected -bash: syntax error near 'aaa'
45赞 Ti Strga 1/14/2014
在 bash 4.2 中添加了“test”内置的“-v”参数。
23赞 osirisgothra 7/14/2014
-v 参数是作为对 -u shell 选项(名词集)的补充添加的。打开“nounset”(set -u)后,如果 shell 不是交互式的,则 shell 将返回错误并终止。$# 非常适合检查位置参数。但是,命名变量需要其他方法,而不是 clobbering,以将它们标识为未设置。不幸的是,这个功能来得太晚了,因为许多人必然会与早期版本兼容,在这种情况下,他们无法使用它。唯一的其他选择是使用技巧或“黑客”来绕过它,如上所示,但它是最不优雅的。
61赞 Joe 7/25/2018
这就是为什么我在被接受/得票最高的答案之后继续阅读的原因。
10赞 Amit Naidu 7/24/2019
这是标量的正确方法,在 ksh 中也可用。注意:如果您不想浪费半个小时盯着屏幕想知道发生了什么,那就不是了。除了验证它是否已设置外,您通常还会查找特定值,例如 .在这种情况下,我更喜欢使用压缩[[ -v foo ]][[ -v $foo ]]123[[ ${foo-123} -eq 123 ]][[ -v foo && $foo -eq 123 ]]
38赞 Seamus Connor 8/27/2013 #15

在现代版本的 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赞 chepner 11/9/2014
另请注意,这并不是不正确的,只是执行了不同的测试。假设;然后,它检查是否设置了名为 Variable 的变量。[ -v "$VARNAME" ]VARNAME=foofoo
5赞 kzh 1/2/2015
请注意,对于那些想要可移植性的人来说,不符合 POSIX 标准-v
0赞 koppor 3/28/2017
如果得到 ,则必须确保使用 而不是 .[: -v: unexpected operatorbashsh
-2赞 mijnnaam 9/7/2013 #16

我总是使用这个,因为任何第一次看到代码的人都很容易理解它:

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

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

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

就是这样。

评论

5赞 HelloGoodbye 11/28/2013
该问题询问如何检查变量是否已设置,而不是变量是否为空。
0赞 solidsnack 11/30/2013
您可能需要使用 而不是 .后者实际上是一个外部命令,并且无法查看变量是否被设置。[[ ... ]][ ... ]
5赞 kevinarpe 11/9/2013 #17

启用 Bash 选项时,上述答案不起作用。此外,它们不是动态的,例如,如何测试名称为“dummy”的变量?试试这个:set -u

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 12/17/2014
我很想知道为什么这个答案被否决了......对我来说效果很好。
0赞 11/30/2015
在 Bash 中,您可以在没有 eval: 更改为间接评估的情况下执行相同的操作: .eval "[ ! -z \${$1:-} ]"[ ! -z ${!1:-} ];
0赞 kevinarpe 11/30/2015
@BinaryZebra:有趣的想法。我建议你作为一个单独的答案发布。
0赞 tetsujin 5/11/2017
使用“eval”使该解决方案容易受到代码注入的影响: $ is_var_defined 'x} ];回声“陷阱”>&2;#' 明白了
0赞 user1261322 11/18/2013 #18

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

if [[ $1 = "" ]]
then
  echo '$1 is blank'
else
  echo '$1 is filled up'
fi

为什么会这样?中的所有内容都存在于 Bash 中,但默认情况下它是空白的,因此无法帮助您。$@test -ztest -n

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

if [ ${#1} = 0 ]
then
  echo '$1 is blank'
else
  echo '$1 is filled up'
fi

评论

0赞 12/1/2013
我知道,但仅此而已。如果某些内容为空,则会出现错误!不好意思。:P
2赞 Zombo 5/11/2014 #19
[[ $foo ]]

(( ${#foo} ))

let ${#foo}

declare -p foo
3赞 kenorb 9/22/2014 #20

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

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

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
11赞 ABu 10/19/2014 #21

使用是了解变量是否已设置的最简单方法,但该选项不区分未设置的变量和设置为空字符串的变量:[[ -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"

对于参数(例如,检查参数是否存在):$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
4赞 Aquarius Power 11/16/2014 #22

我的首选方式是这样的:

$ 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

所以基本上,如果一个变量被设置了,它就变成了“结果的否定”(what will be = “is set”)。falsetrue

而且,如果它未设置,它将成为“结果的否定”(正如空结果的计算结果所计算的那样)(因此将以 being = “NOT set”结束)。truetruefalse

3赞 fr00tyl00p 12/11/2014 #23

这是我每天使用的:

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

这在 Linux 和 Solaris 到 Bash 3.0 下运行良好。

$ myvar="TEST"
$ is_set myvar ; echo $?
0

$ myvar=
$ is_set myvar ; echo $?
0

$ unset myvar
$ is_set myvar ; echo $?
1

评论

1赞 11/30/2015
从 Bash 2.0+ 开始,可以使用间接扩展。您可以将长而复杂(也包括 eval)替换为等效项:test -n "$(eval "echo "\${${1}+x}"")"[[ ${!1+x} ]]
0赞 Jonathan H 3/6/2021
这也是我使用的,是邪恶的,但这适用于我必须使用的所有版本的 bash 和 zsh。eval
0赞 midnite 2/12/2023
是否有可能摆脱并使其符合POSIX标准?[[
0赞 midnite 2/13/2023
谢谢。根据您的回答,我编写了一个符合 POSIX 的版本函数。 必须注意的是,我们应该将变量 NAME(不带 $)作为文本传递以进行检查。isset() { [ -n "$1" ] && [ -n "$(eval printf '%s' "\${$1+x}" || true)" ]; }
0赞 Graham 2/12/2015 #24
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

评论

11赞 Celeo 2/12/2015
欢迎使用 StackOverflow。虽然您的回答值得赞赏,但最好不要只提供代码。你能在你的答案中加入推理或一个小的解释吗?有关扩展答案的其他想法,请参阅此页面
15赞 AlejandroVD 2/8/2016 #25

在 Bash 中,您可以在内置的-v[[ ]]

#! /bin/bash -u

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

echo $SOMEVAR

评论

0赞 Peter Mortensen 3/28/2022
它应该做什么?
54赞 Mark Haferkamp 2/15/2016 #26

注意

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

简答

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

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

为什么这样做

在 Bash 中(至少可以追溯到 3.0),if 是一个声明/设置的变量,然后输出一个命令,将变量设置为其当前类型和值的任何类型,并返回状态代码(成功)。如果未声明,则向 输出错误消息并返回状态代码。使用 ,将常规和输出重定向到 ,永远不会被看到,并且不更改状态代码。因此,该函数仅返回状态代码。vardeclare -p vardeclarevar0vardeclare -p varstderr1&>/dev/nullstdoutstderr/dev/null

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

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

当此方法在 Bash 中失败时

这仅适用于命名变量(包括),不适用于某些特殊变量(、由于这些都不是数组,因此 POSIX 样式适用于所有这些特殊变量。但要注意不要将其包装在函数中,因为许多特殊变量在调用函数时会更改值/存在。$_$!$@$#$$$*$?$-$0$1$2[ -n "${var+x}" ]

Shell 兼容性说明

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

如果你需要你的脚本与 POSIX sh 兼容,那么你就不能使用数组。没有数组,可以工作。[ -n "${var+x}" ]

Bash 中不同方法的比较代码

此函数取消设置变量,传递代码,运行测试以确定是否由 d 代码设置,最后显示不同测试的结果状态代码。varevalvareval

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

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 对于测试 Bash 中的命名变量是可靠的(100%?),至少从 3.0 开始。
  • [ -n “${var+x}” ] 在符合 POSIX 的情况下是可靠的,但不能处理数组。
  • 存在其他测试,用于检查变量是否为非空,以及检查其他 shell 中声明的变量。但这些测试既不适合 Bash 脚本,也不适合 POSIX 脚本。

评论

5赞 user1387866 3/15/2019
在我尝试过的答案中,这个 () 是唯一有效的答案。谢谢!declare -p var &>/dev/null
0赞 joehanna 11/24/2019
此外,使用不带 $ 前缀的变量名称也有效:返回 0,其中 as 返回 1。(在 bash 5.0.11(1) 版本上)declare -p PATH &> /dev/nulldeclare -p ASDFASDGFASKDF &> /dev/null
1赞 xebeche 11/26/2019
干得好!1 个警告:声明 var 是一个变量,但它没有根据以下方式设置它: 测试方式declare varset -o nounsetdeclare foo bar=; set -o nounset; echo $bar; echo $foo
0赞 Mark Haferkamp 12/2/2019
@joehanna 谢谢你抓住了那个失踪的人!我修复了它,但我认为逻辑令人困惑,足以保证一个功能,特别是考虑到 @xebeche 的评论。@xebeche 这对我的回答很不方便......看起来我必须尝试类似 or ./declare -p "$1" | grep =test -v "$1"
0赞 Mark Haferkamp 12/2/2019
@xebeche 经过更多测试,似乎具有与 相同的真值,因此唯一的区别是导致脚本/子 shell 退出。也就是说,我同意我的回答应该解释设置变量与仅声明变量之间的区别,而不是将这些情况混为一谈。随意编辑。在下一个常年通知之前,我不会回到这个答案。set -o nounset; (echo "$var") &>/dev/null[ -n "${var+x}" ]set -o nounset
0赞 Daniel S 3/1/2016 #27

我喜欢辅助函数来隐藏 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

测试用例还表明它没有声明 var(除非后跟 '=')。在很长一段时间里,我以为我以这种方式声明变量,只是现在发现我只是表达了我的意图......我猜这是一个无操作。local var

IsDeclared_Tricky xyz: 1 IsDeclared_Tricky foo: 1 IsDeclared_Tricky bar: 0 IsDeclared_Tricky baz: 0 IsDeclared xyz:1 IsDeclared foo:1 IsDeclared 柱线: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 the error
  exampleFunction
  echo $outputStr
}

bonus

如果在声明所有必需变量的情况下调用:

世界您好!

还:

错误:未声明变量:outputStr

-1赞 Martin Rüegg 3/12/2016 #28

检查变量是否已声明/未设置的函数

包括空的$array=()


以下函数测试给定名称是否作为变量存在

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • 通过首先测试变量是否设置(未)设置,可以避免对声明的调用(如果不需要)。
  • 但是,如果包含空的名称,则调用声明将确保我们得到正确的结果$1$array=()
  • 传递给 /dev/null 的数据永远不会太多,因为只有在变量未设置或空数组时才会调用 claim。

此函数将按照以下条件进行测试:

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset

.

更多细节

和测试脚本,请参阅我对“如何检查 bash 中是否存在变量?

备注:Peregring-lk 的回答也表明了类似的用法,这确实是巧合。否则我当然会相信它!
declare -p

27赞 RubenLaguna 11/16/2016 #29

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

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

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

评论

0赞 Palec 11/16/2016
您缺少冒号。它应该是 ,而不是 .但是在 Bash 中,我可以确认即使没有冒号它也能正常工作。${var:-}${var-}
7赞 RubenLaguna 11/16/2016
${var:-}和意思不同。 如果未设置或 null,则将扩展为空字符串,并且仅在未设置时将扩展为空字符串。${var-}${var:-}var${var-}
0赞 Palec 11/16/2016
刚刚学到了一些新东西,谢谢!省略冒号将导致仅对未设置的参数进行测试。换句话说,如果包含冒号,运算符将测试两个参数是否存在,并且其值是否为 null;如果省略冒号,则运算符仅测试是否存在。这里没有区别,但很高兴知道。
26赞 Jarrod Chesney 12/9/2016 #30

如果未设置,则要退出

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

#!/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}“是微不足道的, 它可以是任何东西(如 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(评论),他们挑战我准确回答这个问题。

参考: 2.6.2 参数扩展

179赞 deizel. 6/17/2017 #31

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

参数扩展 VARIABLE设置 VARIABLE VARIABLE未凝固的
${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" "" ""

请注意,每个组(带和不带前面的冒号)都具有相同的 setunset 大小写,因此唯一不同的是如何处理空大小写。

对于前面的冒号,大小写和未设置的大小写是相同的,所以我会尽可能使用它们(即使用 ,而不仅仅是 ,因为空大小写不一致)。:==

标题:

  • set means 为非空 (VARIABLEVARIABLE="something")
  • empty 表示为空/null (VARIABLEVARIABLE="")
  • unset 表示不存在 (VARIABLEunset VARIABLE)

值:

  • $VARIABLE表示结果是变量的原始值。
  • "default"表示结果是提供的替换字符串。
  • ""表示结果为 null(空字符串)。
  • exit 127表示脚本停止执行,退出代码为 127。
  • $(VARIABLE="default")表示结果是 并且(以前为空或未设置)也将设置为等于 。"default"VARIABLE"default"

评论

1赞 Palec 6/18/2017
您只修复了实际编码错误(使用变量时不需要的间接实例)。我做了一个编辑,以修复更多美元在解释描述时会有所不同的情况。顺便说一句,所有 shell 变量(数组除外)都是字符串变量;它们可能只是包含一个可以解释为数字的字符串。谈论字符串只会使信息变得模糊。引号与字符串无关,它们只是转义的替代方法。 可以写成 .不过,前者更具可读性。VARIABLE=""VARIABLE=
0赞 Astronaut 5/21/2020
感谢您的表格,它非常有用,但是如果未设置或为空,我正在尝试让脚本退出。但是我得到的退出代码是 1 而不是 127。
0赞 Tom 11/9/2020
未设置时,我看到它等于 ,而不是 的原始值(未设置)。你的描述正确吗?也许我误读了你所说的原始价值。VARIABLEA=${VARIABLE=default}A"default"VARIABLE$(VARIABLE="default")
1赞 Tom 11/9/2020
根据文档,它说“在所有情况下,参数的最终值都应被替换。因此,也许应该将描述更改为:表示结果是 ,这将设置为等于 too。$(VARIABLE="default")"default"VARIABLE"default"
1赞 Peter Mortensen 3/28/2022
使用新表功能的候选项。
0赞 Hatem Jaber 6/13/2019 #32

在浏览了所有答案之后,这也有效:

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$

评论

0赞 Michael Heuberger 8/30/2019
当您有效时将不起作用,这将打印错误set -uunboud variable
31赞 infokiller 6/5/2020 #33

总结

  • 用于检查变量是否不为空(因此也必须定义/设置)。用法:test -n "${var-}"

    if test -n "${var-}"; then
      echo "var is set to <$var>"
    else
      echo "var is not set or empty"
    fi
    
  • 用于检查变量是否已定义/设置(即使它为空)。用法:test -n "${var+x}"

    if test -n "${var+x}"; then
      echo "var is set to <$var>"
    else
      echo "var is not set"
    fi
    

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

笔记

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

解释

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

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

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

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

评论

0赞 user3750888 1/29/2022
看起来您不小心在对 的解释中包含了一个“not”。在测试的手册页中: 如果 STRING 的长度为零,则 -z STRING 返回 true。因此,如果 var 未定义或为空,将返回 true。test -ztest -z "${var-}"
-1赞 Christopher King 10/23/2020 #34

声明一个简单的函数,用于直接测试变量是否存在。is_setdeclare -p

$ is_set() {
    declare -p $1 >/dev/null 2>&1
}

$ is_set foo; echo $?
0

$ declare foo

$ is_set foo; echo $?
1
20赞 Cameron Hudson 12/29/2020 #35

为了清楚地回答 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       |
+-----------------------+-------------+---------+------------+

顺便说一句,这些表达式是等价的: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       |
+----------------------+-------------+---------+------------+
12赞 Jean-Baptiste Poittevin 1/21/2021 #36

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

要测试变量是否按名称设置,请执行以下操作。[ ${!name+x} ]

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

这个答案几乎与莱昂内尔的答案相似,但通过省略 .-z

要测试是否设置了命名变量,请执行以下操作:

function is_set {
    local v=$1
    echo -n "${v}"
    if [ ${!v+x} ]; then
        echo " = '${!v}'"
    else
        echo " is unset"
    fi
}

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

function a {
    if [ ${1+x} ]; then
        local arg=$1
        echo "a '${arg}'"
    else
        echo "a: arg is unset"
    fi
}

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

set -eu

V1=a
V2=
V4=-gt
V5="1 -gt 2"
V6="! -z 1"
V7='$(exit 1)'

is_set V1
is_set V2
is_set V3
is_set V4
is_set V5
is_set V6
is_set V7

a 1
a
a "1 -gt 2"
a 1 -gt 2
$ ./test.sh

V1 = 'a'
V2 = ''
V3 is unset
V4 = '-gt'
V5 = '1 -gt 2'
V6 = '! -z 1'
V7 = '$(exit 1)'
a '1'
a: arg is unset
a '1 -gt 2'
a '1'

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

16赞 Mike Clark 4/26/2021 #37

我很惊讶没有人尝试编写一个 shell 脚本来以编程方式生成臭名昭著的难以摸索的表。既然我们在这里尝试学习编码技术,为什么不用代码表达答案呢?:)这是我的看法(应该适用于任何 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
#                                  "${N:?x}" "${U:?x}  "
printf "$R" "\${FOO:?x}" "${S:?x}" "<error>" "<error>  "; S='V';N='';unset U
#                                            "${U?x}   "
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" "----------" "-------" "-------" "---------";

以及运行脚本的输出:

+------------+------------+------------+------------+
| 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          |            |
+------------+------------+------------+------------+

该脚本缺少一些功能,例如显示副作用分配何时发生(或不发生),但也许其他更雄心勃勃的人想要采取这个起点并使用它。

评论

2赞 Mike 7/30/2021
干得好,迈克。它看起来很棒。就像你说的,它并不完全全面,但它很快就可以找到并具有可比性。
15赞 Shodan 10/16/2022 #38

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

“如果设置了变量,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