3

bash manual says:

A parameter is an entity that stores values

A variable is a parameter denoted by a name.

A parameter is set if it has been assigned a value.

An array variable is considered set if a subscript has been assigned a value.

Is an array variable a variable and a parameter?

If yes, why is an array variable not considered set if it is assigned ()?

$ ar=()
$ echo ${ar-This is a new value}
This is a new value

Thanks.

1

3 Answers 3

4

In bash, like in ksh, where $var is an array, $var is actually short for ${var[0]}.

So in:

var=([1]=whatever [2]=blah)

${var+set} would not expand to set, and [[ -v var ]] would return false, because $var has no element of indice 0 (note that in bash, like in ksh, but contrary to most other shells and languages, arrays are sparse arrays, or associative arrays with keys limited to positive integers).

In bash (at least since 4.3), when you declare a variable as array with:

typeset -a var

var is initially unset. It's not even set to an empty list. In 4.3, typeset -p var would return an error. In 4.2 and before it would output:

declare -a a='()'

(as if declaring the array assigned an empty list like it does in zsh)

In 4.4, it outputs:

declare -a var

So, since 4.3, an unset (but declared) array is different from an array that is assigned an empty list.

Still, it's difficult programmatically to differentiate between both. But in practice, the distinction is not very useful. Most likely, what you want to know is how many elements are in the array:

((${#array[@]})) || echo array has no element

See zsh, rc, fish and yash for shells with better array designs.

6
  • Thanks. ${var+set} treats var as var[0]. Does $(parameter+word expect parameter to be a nonarray variable? If not, how can I specify parameter to represent an array?
    – Tim
    Jul 24, 2017 at 19:39
  • In 4.4, when run unset var and declare -i var, then run declare -p var. Does it output error such as bash: declare: var: not found or declare -i var? I would like to see if it treats array differently from other types. I have 4.3 only.
    – Tim
    Jul 24, 2017 at 23:08
  • That is not the correct way to test an array: ${var[@]+set} is (:+ fails on ksh93). What you used only test index 0.
    – user232326
    Jul 24, 2017 at 23:38
  • In bash: typeset -p var would return an error only in 4.3, It was correct before and became correct with 4.4 again.
    – user232326
    Jul 24, 2017 at 23:49
  • @Tim, it outputs declare -i var. Jul 25, 2017 at 7:05
2

The construct ${var-foo} expands to foo if var is unset or null. An empty array is exactly that.

As you quote:

An array variable is considered set if a subscript has been assigned a value.

The example you site (ar=()) does not assign a value to any subscripts.

1
  • But ${var-foo} doesn't check whether the array is set but whether the element of indice 0 (beware it's not necessarily the same as the first element as bash arrays are sparse) is set. Jul 25, 2017 at 12:26
2

The correct way to test for a whole array is:

[[ ${ar[@]+set} == set ]] && echo "array has a value may be a null"

What you wrote is the test for the value at index 0. This two are equivalent:

${ar+set}
${ar[0]+set}

But both will only test whether the array at index zero is set or not, not the whole array, which I assume is what you meant.

But, to your question: Why is an array unset if asigned ()?

Because you have not asigned any value, not even the null, to any array index.
To get some index to be set (and consequently the array) you need something like:

$ declare -a ar=([3]="value")

Or even:

$ declare -a ar=([3]="")

zsh (even in sh or ksh emulation) is the only one of all shells tested (as usual, the special one) that declare an array as set with:

$ unset ar; typeset -a ar=(); echo $(typeset -p ar) ${ar[@]+set}

Question from comments:

Thanks. Can ar[@] be replaced with ar[*] here? Are they array variables?

No, an "${ar[*]}" is the string concatenation of all array values.

However, in this specific expansion: "${ar[*]+set}" both are equivalent.

From the manual (at Arrays):

If subscript is @ or *, the word expands to all members of name.

10
  • Thanks. Can ar[@] be replaced with ar[*] here? Are they array variables?
    – Tim
    Jul 24, 2017 at 23:56
  • @Tim Answer edited.
    – user232326
    Jul 25, 2017 at 0:17
  • Good point about ${a[@]+set} (see also [[ -v a[@] ]] (in bash, not in ksh)). About a=(); echo "${a[@]+set}", yash (another one of the shells with the better array designs) also outputs set. In ksh93, a=(); echo "${a+set}", which declares an empty compound variable, also returns set which makes it even less consistent there. bash just copied ksh. Jul 25, 2017 at 6:33
  • No, in this case, bash does not act as ksh93 (in none version). With a=(); echo "${a+set}" ksh outputs set (and that is incorrect), bash don't (and that is correct). The difference between ksh and bash is still present with a=(); echo "${a[@]+set}". All shells correctly report set with a=(""); echo "${a[@]+set}", so, it is just a matter of correcting a bug of ksh with no index set (not even to null).
    – user232326
    Jul 25, 2017 at 6:52
  • @Arrow, in ksh93, a=() doesn't declare an array but a compound variable. It is set because it is not unset, it's been assigned a value (the empty list). Arrays receive a special treatment probably for backward compatibility. In ksh88, you'd set arrays with set -A a x y very similarly to Bourne's setting of the $@ array with set x y. There was no typeset -a, set -A a was the same as unset a. That's still the case in ksh93 though it has typeset -a which allows to stick an array attribute to a variable. If you don't use typeset, a=(1 2); a=($()) still unsets the variable. Jul 25, 2017 at 12:39

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .