7

I'm writing a script and I discovered some unexpected behaviour of uninitialized and unset array variables that I do not understand.

First of all, the length:

$ echo ${#notset[@]}
0
$ uninitialized=
$ echo ${#uninitialized[@]}
1

Why is the uninitialized length 1? Shouldn't it be zero? Is it because a null variable is considered an array of one null element?

This fact leads to some problems. For example suppose I want to create an array, and insert a certain number of things based on the user command line arguments. I thought I could do something like(+):

myarray=

if [ some-condition ]
then
    myarray[${#myarray[@]}]=some-value
fi

if [ some-condition2 ]
then
    myarray[${#myarray[@]}]=some-value2
elif [ some-condition3 ]
then
    myarray[${myarray[@]}]=some-value3
    myarray[${myarray[@]}]=some-value4
fi

But this leaves the first slot to null, which I do not like and also breaks some code that I have written(*), and at this point suppose that I want to see if the array contains any element. How should I do it?

[ -z "${myarray[@]}" ]

Raises an error if the array contains more than an element.

[ -z "$myarray" ]

Fails because the first element is null, even if the array is not empty.

So, how should I control that an array is uninitialized?

And could someone explain what exactly happens when dealing with arrays and unset - uninitialized variables?


(+) I know that I could avoid "declaring" the variable and it would work, but this script will be reviewed by a professor, and he does not like variables being defined at random places.

(*) Before trying this thing I was keeping the length of the array in an other variable, and so I did not have problems. But I'd like to avoid defining this auxiliary variables since I know I can obtain the length without them.

1
  • Note that uninitialized is not uninitialized; its first element is the empty string. foo and ${foo[0]} are nearly interchangeable.
    – chepner
    Commented Apr 22, 2014 at 18:38

2 Answers 2

11

You can see the difference with declare -p:

unset foo
declare -a foo
declare -p foo
# prints declare -a foo='()'
foo=
declare -p foo
# prints declare -a foo='([0]="")'

If you want to initialize an empty array, the output of the first declare -p is a good hint on the best way to declare it:

declare -a array='()'

(The declare -a part is probably optional, a simple array=() should work just as well.)

If you want to test if an array has 0 elements, use numeric comparison on ${#array[@]}; don't try to do a test -z on the expansion as won't give the correct result in many cases.

2
  • I did not know of declare -p. Thank you for solving my doubts. The only thing that is not clear to me is your last sentence. I always used the ${#array[@]} notation, why did you tell me to not use the other one?
    – Bakuriu
    Commented Oct 26, 2012 at 17:37
  • 1
    @Bakuriu I was just trying to say that to test if an array is empty, it is easier to test if the count provided by ${#array[@]} is 0 than trying to expand the array contents and testing if the resulting string is empty.
    – jw013
    Commented Oct 26, 2012 at 17:47
8

To initialize an empty array, use

array=()

To add a value to the array, use

array+=(value)
2
  • Great, I did not know of this syntax. The only thing is that this requires to use declare -a var to avoid having the null at the beginning.
    – Bakuriu
    Commented Oct 26, 2012 at 17:38
  • 1
    @Bakuriu: Initialize it as array=().
    – choroba
    Commented Oct 26, 2012 at 17:45

You must log in to answer this question.

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