Bash – Difference Between `declare foo` and `foo=`

bash

What's the difference between declare foo and foo= in Bash?

If I print out the value of foo, then it's foo and foo="" for declare foo and foo=, respectively.

Are the values of foo and foo="" equivalent? If not, how is it possible to differ between these values (both -n and -z for [[ ... ]] behaves identically)?

$ foo=
$ declare -p foo
declare -- foo=""
$ declare -p bar
bash: declare: bar: not found
$ declare bar
$ declare -p bar
declare -- bar

Best Answer

Are the values of foo and foo="" equivalent? If not, how is it possible to differ between these values

No, the former is pretty much like an unset variable. You could use something like declare -x foo to set flags to a variable without setting a value, e.g. here, to mark foo as exported in case it ever gets a value. If you can find a use for that. (It won't be actually exported to commands without a value.)

Use [[ -v name ]] (Bash/ksh/etc.) or [ "${var+set}" = set ] (standard) to tell the difference between an unset and a set-but-empty variable:

$ unset foo
$ declare foo
$ declare -p foo
declare -- foo
$ [[ -v foo ]] && echo is set || echo not set
not set
$ echo ${foo+set}

$ unset foo
$ foo=
$ declare -p foo
declare -- foo=""
$ [[ -v foo ]] && echo is set || echo not set
is set
$ echo ${foo+set}
set

Also, using declare var in a function makes the variable local to the function, a straight assignment doesn't, it assigns to the global variable instead:

$ foo=123 
$ f() { declare foo; declare -p foo; }
$ f; declare -p foo
declare -- foo
declare -- foo="123"

$ g() { foo=; declare -p foo; }
$ g; declare -p foo
declare -- foo=""
declare -- foo=""

Going outside Bash, it seems that Ksh is similar to Bash here, and that in Zsh, there's no difference between foo= and typeset foo, the latter also sets the variable to the empty string:

% unset foo; typeset foo; typeset -p foo; [[ -v foo ]] && echo is set || echo not set
typeset foo=''
is set

% unset foo; foo=; typeset -p foo; [[ -v foo ]] && echo is set || echo not set
typeset foo=''
is set

See also: