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

2359

我如何在Bash中知道一个变量是否已经设置?

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

function a {
    # if $1 is set ?
}

17
如果测试 $# 大于 0,则打印“arg <参数>”。 - Jens
284
注意寻求解决方案的人:对于这个问题,有许多评价很高的答案回答了变量是否为空的问题。更正式的解决方案(即“变量是否设置”)在Jens和Lionel回答中提到。 - Nathan Kidd
12
Russell Harmon和Seamus的"-v"测试是正确的,尽管这似乎只在新版本的“bash”中可用,而且不能在不同的shell中移植。 - Graeme
6
正如@NathanKidd所指出的,Lionel和Jens提供了正确的解决方案。prosseek,您应该将您接受的答案切换为其中之一。请参考此处 - Garrett
4
...否则,对于我们中更有眼光的人来说,错误的答案可能会被踩,因为@prosseek没有解决问题。 - dan3
显示剩余8条评论
38个回答

11

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

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

根据变量类型来检查最佳,包括环境变量、参数或正常变量。

对于环境变量:

[[ $(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的输出,而不影响其返回代码。

10

您可以做以下事情:

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

11
你可以使用 -n 而不是取反 -z - Dennis Williamson
1
你需要在 $1 周围加上引号,即 [ ! -z "$1" ]。或者在 bash/ksh/zsh 中写成 [[ ! -z $1 ]] - Gilles 'SO- stop being evil'
5
如果$1被设置为空字符串,这将会失败。 - HelloGoodbye

5

当Bash选项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"模式下定义


这篇文章涉及到IT技术方面的内容。

1
我想知道为什么这个回答被踩了...这对我来说很有效。 - LavaScornedOven
在Bash中,您可以不使用eval来完成相同的操作:将eval "[ ! -z \${$1:-} ]"更改为间接评估:[ ! -z ${!1:-} ]; - user2350426
@BinaryZebra:有趣的想法。我建议你单独发布一个答案。 - kevinarpe
使用“eval”使此解决方案容易受到代码注入攻击:$ is_var_defined 'x}]; echo"gotcha">&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 = “未设置”。

3
在shell中,您可以使用-z运算符,该运算符在字符串长度为零时为True。
如果未设置默认值MY_VAR,则可以使用简单的一行代码来设置它,否则可以选择显示消息。
[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."

3

这是我每天都在使用的:

#
# 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
自从Bash 2.0+版本以后,间接扩展功能就可用了。你可以使用等价的[[ ${!1+x} ]]来替换长而复杂(包括eval)的test -n "$(eval "echo "\${${1}+x}"")" - user2350426
这也是我使用的方法,“eval”很危险,但这个方法适用于我所使用过的所有版本的bash和zsh。 - Jonathan H
是否有可能摆脱 [[ 并使其符合 POSIX 标准? - midnite
谢谢。根据您的回答,我编写了一个符合POSIX标准的版本函数。isset() { [ -n "$1" ] && [ -n "$(eval printf '%s' "\${$1+x}" || true)" ]; } 需要注意的是,我们应该将变量名称(不带$)作为文本进行检查。 - midnite

2
[[ $foo ]]

或者

(( ${#foo} ))

或者

let ${#foo}

或者

declare -p foo

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 var不会声明变量(除非后面跟着'=')。有一段时间我以为是这样声明变量的,现在才发现我只是表达了我的意图...我猜这是一个无操作。

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

bonus

如果所有必需的变量都已声明,则调用如下:

Hello,World!

否则:

错误:未声明变量:outputStr。


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

11
欢迎来到StackOverflow。虽然我们感谢您的回答,但最好不仅提供代码,还要附加一些解释或小说明。请查看此页面获取有关扩展答案的其他想法:http://stackoverflow.com/help/how-to-answer。 - Celeo

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,它会打印出未定义变量错误,这时代码将无法运行。 - Michael Heuberger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接