前几天开发遇到的诡异问题,最后总结下来就是标题所描述的case,怎么判断一个bash的variable到底是unset (undefined)还是set (defined)了只是正好是empty (“”)呢? 为了避免环境不同以及软件版本不同造成更大的confusion,这里先统一一下环境,以下命令或script都在如下环境下运行: [cc lang=”bash” nowrap=”false”] $ 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 [/cc] [cc lang=”bash”] $ bash –version GNU bash, version 3.2.48(1)-release (x86_64-apple-darwin10.0) Copyright (C) 2007 Free Software Foundation, Inc. [/cc] [Source of the confusion: “ -z “] 第一个solution是test的-z,也是confusion产生的根源。 test的manual这样解释-z: [cc lang=”bash”] NAME test, [ – condition evaluation utility SYNOPSIS test expression [ expression ] DESCRIPTION -z string True if the length of string is zero. [/cc] 想来它的意思是指-z判断的是variable定义了只是正好是zero length (“”),有实例为证: [cc lang=”bash”] $ foobar=”” $ [[ -z $foobar ]] && echo “foobar is empty” foobar is empty $ foobar=”awesome” $ [[ -z $foobar ]] || echo “foobar is $foobar” foobar is awesome [/cc] 故事本该到此结束,但是IBM developerworks的一篇文章打破了和谐,文章里面有一个例子: [cc lang=”bash”] if [ -z “$myvar” ] then echo “myvar is not defined” fi [/cc] 言下之意就是-z是undefined,由于文章较老(01 Apr 2000, April Fools’ Day? ),必须先实践一下才可决断,将信将疑的试了把: [cc lang=”bash”] $ 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) [/cc] 于是,如你所见,confuse产生,以至于接下来google到的任何信息都变的不太可信…… [拨开云雾见天日] 其实前面我说-z是source of the confusion略欠公平,真正的culprit其实是bash的option:nounset . [cc lang=”bash”] $ 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 [/cc] 如上可见,默认的nounset是off的,那究竟nounset是什么意思呢,man bash告诉了我们真相只有一个: [cc lang=”bash” nowrap=”false”] 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. … … [/cc] 原来默认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。 [cc lang=”bash”] $ 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 [/cc] [cc lang=”bash”] $ set -o nounset $ foobar=”” $ [[ -z $foobar ]] && echo “foobar is empty” foobar is empty $ foobar=”awesome” $ [[ -z $foobar ]] || echo “foobar is $foobar” foobar is awesome [/cc]