7

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
ilkkachu
  • 133,243
  • 15
  • 236
  • 397
Shuzheng
  • 4,023
  • 1
  • 31
  • 71
  • [This is related](https://unix.stackexchange.com/questions/254367/in-bash-scripting-whats-the-different-between-declare-and-a-normal-variable), but doesn't answer your question. – pLumo Mar 08 '21 at 09:26
  • Good question! **Maybe** something like `declare` only "declares" the variable name, while `foo=` will assign the null string (compare `man bash` -> `If value is not given, the variable is assigned the null string.`). But a similar statement is not given for `declare` and `od` returns the same output for both assignments ... So, I'm lost here. – pLumo Mar 08 '21 at 09:30

1 Answers1

11

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:

ilkkachu
  • 133,243
  • 15
  • 236
  • 397