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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3601515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 19:37:48  来源:igfitidea点击:

How to check if a variable is set in Bash?

bashshellvariables

提问by prosseek

How do I know if a variable is set in Bash?

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

For example, how do I check if the user gave the first parameter to a function?

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

function a {
    # if  is set ?
}

回答by Lionel

(Usually) The right way

(通常)正确的方法

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

where ${var+x}is a parameter expansionwhich evaluates to nothing if varis unset, and substitutes the string xotherwise.

where${var+x}参数扩展,如果var未设置,则评估为空,x否则替换字符串。

Quotes Digression

行情题外话

Quotes can be omitted (so we can say ${var+x}instead of "${var+x}") because this syntax & usage guarantees this will only expand to something that does not require quotes (since it either expands to x(which contains no word breaks so it needs no quotes), or to nothing (which results in [ -z ], which conveniently evaluates to the same value (true) that [ -z "" ]does as well)).

引号可以被省略(所以我们可以说${var+x}而不是"${var+x}"),因为这种语法和用法保证这只会扩展到不需要引号的东西(因为它要么扩展到x(不包含分词,所以不需要引号),或者到什么都没有(这会导致[ -z ],这很方便地评估为相同的值(真),[ -z "" ]也一样))。

However, while quotes can be safely omitted, and it was not immediately obvious to all (it wasn't even apparent to the first author of this quotes explanationwho is also a major Bash coder), it would sometimes be better to write the solution with quotes as [ -z "${var+x}" ], at the very small possible cost of an O(1) speed penalty. The first author also added this as a comment next to the code using this solution giving the URL to this answer, which now also includes the explanation for why the quotes can be safely omitted.

然而,虽然可以安全地省略引号,并且对所有人来说并不是立即显而易见的(对于同样是主要 Bash 编码器的这个引号解释的第一作者来说甚至不明显),有时编写解决方案会更好用引号 as [ -z "${var+x}" ],以 O(1) 速度损失的非常小的可能成本。第一作者还在使用此解决方案的代码旁边添加了此注释,给出了此答案的 URL,现在还包括解释为什么可以安全地省略引号。

(Often) The wrong way

(经常)走错路

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

This is often wrong because it doesn't distinguish between a variable that is unset and a variable that is set to the empty string. That is to say, if var='', then the above solution will output "var is blank".

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

The distinction between unset and "set to the empty string" is essential in situations where the user has to specify an extension, or additional list of properties, and that not specifying them defaults to a non-empty value, whereas specifying the empty string should make the script use an empty extension or list of additional properties.

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

The distinction may not be essential in every scenario though. In those cases [ -z "$var" ]will be just fine.

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

回答by mbrannig

To check for non-null/non-zero string variable, i.e. if set, use

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

if [ -n "" ]

It's the opposite of -z. I find myself using -nmore than -z.

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

You would use it like:

你会像这样使用它:

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

回答by Jens

Here's how to test whether a parameter is unset, or empty ("Null")or set with a value:

以下是如何测试参数是unset还是空(“Null”)设置了 value

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

Source: POSIX: Parameter Expansion:

来源:POSIX:参数扩展

In all cases shown with "substitute", the expression is replaced with the value shown. In all cases shown with "assign", parameter is assigned that value, which also replaces the expression.

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

To show this in action:

要在行动中展示这一点:

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

回答by Russell Harmon

While most of the techniques stated here are correct, bash 4.2supports an actual test for the presence of a variable (man bash), rather than testing the value of the variable.

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

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

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

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

Notably, this approach will not cause an error when used to check for an unset variable in set -u/ set -o nounsetmode, unlike many other approaches, such as using [ -z.

值得注意的是,用于检查在未设定变量时这种方法不会导致错误set -u/set -o nounset模式,与许多其它的方法,如使用[ -z

回答by ennuikiller

There are many ways to do this with the following being one of them:

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

if [ -z "" ]

This succeeds if $1 is null or unset

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

回答by deizel

I always find the POSIX table in the other answerslow to grok, so here's my take on it:

我总是发现另一个答案中的 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"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Note that each group (with and without preceding colon) has the same setand unsetcases, so the only thing that differs is how the emptycases are handled.

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

With the preceding colon, the emptyand unsetcases are identical, so I would use those where possible (i.e. use :=, not just =, because the empty case is inconsistent).

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

Headings:

标题:

  • setmeans VARIABLEis non-empty (VARIABLE="something")
  • emptymeans VARIABLEis empty/null (VARIABLE="")
  • unsetmeans VARIABLEdoes not exist (unset VARIABLE)
  • set表示VARIABLE非空 ( VARIABLE="something")
  • empty表示VARIABLE为空/空 ( VARIABLE="")
  • unset表示VARIABLE不存在 ( unset VARIABLE)

Values:

价值观:

  • $VARIABLEmeans the result is the original value of the variable.
  • "default"means the result was the replacement string provided.
  • ""means the result is null (an empty string).
  • exit 127means the script stops executing with exit code 127.
  • $(VARIABLE="default")means the result is the original value of the variable andthe replacement string provided is assigned to the variable for future use.
  • $VARIABLE表示结果是变量的原始值。
  • "default"表示结果是提供的替换字符串。
  • ""表示结果为空(空字符串)。
  • exit 127意味着脚本停止执行,退出代码为 127。
  • $(VARIABLE="default")意味着结果是变量的原始值,并且提供的替换字符串被分配给变量以备将来使用。

回答by phkoester

To see if a variable is nonempty, I use

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

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

The opposite tests if a variable is either unset or empty:

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

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

To see if a variable is set (empty or nonempty), I use

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

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

The opposite tests if a variable is unset:

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

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

回答by Seamus Connor

On a modern version of Bash (4.2 or later I think; I don't know for sure), I would try this:

在现代版本的 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

回答by Paul Creasey

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

Although for arguments it is normally best to test $#, which is the number of arguments, in my opinion.

尽管对于参数,我认为通常最好测试 $#,即参数的数量。

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

回答by Mark Haferkamp

Note

笔记

I'm giving a heavily Bash-focused answer because of the bashtag.

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

Short answer

简答

As long as you're only dealing with named variables in Bash, this function should always tell you if the variable has been set, even if it's an empty array.

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

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

Why this works

为什么这有效

In Bash (at least as far back as 3.0), if varis a declared/set variable, then declare -p varoutputs a declarecommand that would set variable varto whatever its current type and value are, and returns status code 0(success). If varis undeclared, then declare -p varoutputs an error message to stderrand returns status code 1. Using &>/dev/null, redirects both regular stdoutand stderroutput to /dev/null, never to be seen, and without changing the status code. Thus the function only returns the status code.

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

Why other methods (sometimes) fail in Bash

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

  • [ -n "$var" ]:This only checks if ${var[0]}is nonempty. (In Bash, $varis the same as ${var[0]}.)
  • [ -n "${var+x}" ]:This only checks if ${var[0]}is set.
  • [ "${#var[@]}" != 0 ]:This only checks if at least one index of $varis set.
  • [ -n "$var" ]:这仅检查是否${var[0]}为非空。(在 Bash 中,$var${var[0]}.相同)
  • [ -n "${var+x}" ]:这仅检查是否${var[0]}设置。
  • [ "${#var[@]}" != 0 ]这只检查是否至少$var设置了一个索引。

When this method fails in Bash

当此方法在 Bash 中失败时

This only works for named variables (including $_), not certain special variables ($!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., and any I may have forgotten). Since none of these are arrays, the POSIX-style [ -n "${var+x}" ]works for all of these special variables. But beware of wrapping it in a function since many special variables change values/existence when functions are called.

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

Shell compatibility note

外壳兼容性说明

If your script has arrays and you're trying to make it compatible with as many shells as possible, then consider using typeset -pinstead of declare -p. I've read that ksh only supports the former, but haven't been able to test this. I do know that Bash 3.0+ and Zsh 5.5.1 each support both typeset -pand declare -p, differing only in which one is an alternative for the other. But I haven't tested differences beyond those two keywords, and I haven't tested other shells.

如果你的脚本有阵列和你想使它兼容尽可能多的炮弹可能,那么可以考虑使用typeset -p替代declare -p。我读过 ksh 只支持前者,但无法对此进行测试。我确实知道 Bash 3.0+ 和 Zsh 5.5.1 都支持typeset -pdeclare -p,不同之处仅在于哪一个是另一个的替代方案。但是除了这两个关键字之外,我还没有测试过差异,也没有测试过其他 shell。

If you need your script to be POSIX sh compatible, then you can't use arrays. Without arrays, [ -n "{$var+x}" ]works.

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

Comparison code for different methods in Bash

Bash 中不同方法的比较代码

This function unsets variable var, evals the passed code, runs tests to determine if varis set by the evald code, and finally shows the resulting status codes for the different tests.

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

I'm skipping test -v var, [ -v var ], and [[ -v var ]]because they yield identical results to the POSIX standard [ -n "${var+x}" ], while requiring Bash 4.2+. I'm also skipping typeset -pbecause it's the same as declare -pin the shells I've tested (Bash 3.0 thru 5.0, and Zsh 5.5.1).

我跳过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 ""

    # 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' "" $nonempty $plus $count $declared
}

Test case code

测试用例代码

Note that test results may be unexpected due to Bash treating non-numeric array indices as "0" if the variable hasn't been declared as an associative array. Also, associative arrays are only valid in Bash 4.0+.

请注意,如果变量尚未声明为关联数组,则由于 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

Test output

测试输出

The test mnemonics in the header row correspond to [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], and declare -p var, respectively.

该测试在助记符标题行对应于[ -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

Summary

概括

  • declare -p var &>/dev/nullis (100%?) reliable for testing named variables in Bash since at least 3.0.
  • [ -n "${var+x}" ]is reliable in POSIX compliant situations, but cannot handle arrays.
  • Other tests exist for checking if a variable is nonempty, and for checking for declared variables in other shells. But these tests are suited for neither Bash nor POSIX scripts.
  • declare -p var &>/dev/null至少从 3.0.0 开始,在 Bash 中测试命名变量是(100%?)可靠的。
  • [ -n "${var+x}" ]在符合 POSIX 的情况下是可靠的,但不能处理数组。
  • 其他测试用于检查变量是否为非空,以及用于检查其他 shell 中声明的变量。但是这些测试既不适合 Bash 也不适合 POSIX 脚本。