Search code examples
arraysbashinitialization

Initialization of an empty Bash indexed array


I’ve read some of the related questions on the subject but didn’t find the precise one I have in mind. I hope it’s not because it is too stupid.

In Bash 5 (5.2.15 if that matters), does it make any difference declaring an array like:

declare -a foo

compared with:

declare -a foo=()

I wonder if the first declaration does ensure the creation of an empty array, or only the second one guarantees that?

You may ask, what the point in initializing an empty array? Why not initializing it only when it’s needed storing a first element?

Well, I don’t know, maybe?

Then another question arises, would it be safe to initialize the array only when storing the first element with the append operator? Like:

bar+=('baz')

(bar here being not defined before this first element is appended)

it seems not to be a problem, even if option nounset is set, but doing so, am I assured 'baz' will always be the first element in the array? Is it safe to do that?

Also, in the case I’d need to change the default scope of the variable, I guess I couldn’t avoid using declare (or local), and it seems wrong to call declare or local every times I append an element (while it seems to work though).

Sorry if it’s a stupid question or is badly worded.

Have a nice day.


Solution

  • Addressing the declare -a foo vs declare -a foo=() (vs foo=()) question ...

    The results are going to depend on what's in foo prior to the declare -a.

    $ foo=a                                       # set to single character
    $ declare -p foo
    declare -- foo="a"
    
    $ declare -a foo                              # convert to array
    $ declare -p foo
    declare -a foo=([0]="a")                      # maintains initial value of 'a'
    
    
    ---------------------------------
    
    $ foo=a                                       # set to single character
    $ declare -p foo
    declare -- foo="a"
    
    $ declare -a foo=()                           # convert to array but also clear contents
    $ declare -p foo
    declare -a foo=()                             # array is empty
    
    
    ---------------------------------
    
    $ foo=(a b c)                                 # set to 3 element array
    $ declare -p foo
    declare -a foo=([0]="a" [1]="b" [2]="c")
    
    $ declare -a foo                              # (re)declare as array
    $ declare -p foo
    declare -a foo=([0]="a" [1]="b" [2]="c")      # maintains intitial set of 3 values
    
    
    ---------------------------------
    
    $ foo=(a b c)                                 # set to 3 element array
    $ declare -p foo
    declare -a foo=([0]="a" [1]="b" [2]="c")
    
    $ declare -a foo=()                           # (re)declare as array but also clear contents
    $ declare -p foo
    declare -a foo=()                             # array is empty
    

    Findings:

    • if foo is undefined then declare -a foo, declare -a foo=() and foo=() behave the same
    • if foo is predefined then declare -a foo will maintain the predefined value, while declare -a foo=() and foo=() will clear the contents of foo

    If you know with 100% certainty your variable (foo in this case) is undefined then you're safe with declare -a foo or declare -a foo=() or foo=().

    If you're not sure about the contents of the variable (eg, it was exported by the parent process) then you're safer explicitly clearing the variable with declare -a foo=() or foo=().

    If you need the array to be empty then I'd suggest always explicitly defining the array as empty via declare -a foo=() or foo=().

    NOTES:

    • results of these test cases will vary if you initially define foo and then try to define as an associative array (declare -A foo)
    • personally, I prefer to initially run unset foo as this eliminates any unwanted leftover behavior from other declare flags (eg, -x, -r, etc), and then declare and populate the variable
    • in the case of foo=(a b c); declare -A foo you'll generate an error since bash won't convert an 'indexed' array to an associative array ... a good example where unset foo should be run before creating/populating (a new) foo