Bash variable unset or empty?

前几天开发遇到的诡异问题,最后总结下来就是标题所描述的case,怎么判断一个bash的variable到底是unset (undefined)还是set (defined)了只是正好是empty (“”)呢?

为了避免环境不同以及软件版本不同造成更大的confusion,这里先统一一下环境,以下命令或script都在如下环境下运行:

$ uname -a
Darwin mac.local 10.4.0 Darwin Kernel Version 10.4.0: Fri Apr 23 18:28:53 PDT 2010; root:xnu-1504.7.4~1/RELEASE_I386 i386
$ bash --version
GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0)
Copyright (C) 2007 Free Software Foundation, Inc.

[Source of the confusion: ” -z “]
第一个solution是test的-z,也是confusion产生的根源。

test的manual这样解释-z:

NAME
     test, [ -- condition evaluation utility

SYNOPSIS
     test expression
     [ expression ]

DESCRIPTION
     -z string     True if the length of string is zero.


想来它的意思是指-z判断的是variable定义了只是正好是zero length (“”),有实例为证:

$ foobar=""
$ [[ -z $foobar ]] && echo "foobar is empty"
foobar is empty
$ foobar="awesome"
$ [[ -z $foobar ]] || echo "foobar is $foobar"
foobar is awesome

故事本该到此结束,但是IBM developerworks的一篇文章打破了和谐,文章里面有一个例子:

if [ -z "$myvar" ]
then
    echo "myvar is not defined"
fi

言下之意就是-z是undefined,由于文章较老(01 Apr 2000, April Fools’ Day? ),必须先实践一下才可决断,将信将疑的试了把:

$ unset foobar
$ [[ -z $foobar ]] && echo "foobar is not defined (unset)"
foobar is not defined (unset)
$ [[ -z $XXX_foobar ]] && echo "not defined (unset)"
not defined (unset)

于是,如你所见,confuse产生,以至于接下来google到的任何信息都变的不太可信……

[拨开云雾见天日]
其实前面我说-z是source of the confusion略欠公平,真正的culprit其实是bash的option:nounset .

$ set -o
allexport       off
braceexpand     on
emacs           on
errexit         off
errtrace        off
functrace       off
hashall         on
histexpand      on
history         on
ignoreeof       off
interactive-comments    on
keyword         off
monitor         on
noclobber       off
noexec          off
noglob          off
nolog           off
notify          off
nounset         off
onecmd          off
physical        off
pipefail        off
posix           off
privileged      off
verbose         off
vi              off
xtrace          off

如上可见,默认的nounset是off的,那究竟nounset是什么意思呢,man bash告诉了我们真相只有一个:

SHELL BUILTIN COMMANDS
set [--abefhkmnptuvxBCHP] [-o option] [arg ...]
     ... ...
     -o option-name
     ... ...
          nounset Same as -u.
     ... ...
     -u      Treat  unset  variables as an error when performing parameter expansion.  If expansion is attempted on an unset variable, the shell prints an error message, and, if not interactive, exits with a non-zero status.
     ... ...

原来默认bash里面undefined的variable就是empty string,必须explicitly的声明set -o nounset或者set -u才能让unset的variable报错,test的-z确实就如man test所说只检验是否是zero length的string(IBM developerworks上的文章果然是April Fools…),只是bash default的undefined variable恰好就是empty string。

$ set -u
$ unset foobar
$ [[ -z $foobar ]] && echo "foobar is not defined (unset)"
-bash: foobar: unbound variable
$ [[ -z $XXX_foobar ]] && echo "not defined (unset)"
-bash: XXX_foobar: unbound variable
$ set -o nounset
$ foobar=""
$ [[ -z $foobar ]] && echo "foobar is empty"
foobar is empty
$ foobar="awesome"
$ [[ -z $foobar ]] || echo "foobar is $foobar"
foobar is awesome

Related posts:

4 Comments on "Bash variable unset or empty?"

  1. Sunng China Mozilla SeaMonkey Windows says:

    长见识了,原来bash这么复杂啊

  2. Goodtiger China Google Chrome Windows says:

    工作都接触不到

Got something to say? Go for it!

使用新浪微博登陆