Bash arrays are one dimensional variables. They may be one of two types, indexed or associative. Indexed arrays have integer keys and associative arrays have string keys. Values for both are strings.

(Other languages call an associative array a “dictionary”, “hash”, or “map”.)

Initializing

Indexed arrays are declared using declare -a, but can also be implicitly declared (in the global scope) using ARRAY[subscript], where subscript is an arithmetic expression. For this reason, associative arrays must be explicitly declared using declare -A. Initial values may optionally be defined at declaration. The -p option to the declare builtin will print the full variable, including type, keys, and values.

$ declare -a ARRAY                       # explicit declaration of indexed array
$ declare -A MAP                         # explicit declaration of associative array
$ ARRAY[10]=bar                          # implicit (global) declaration
$ declare -a ARRAY=([10]=foo [20]=bar)   # declare and set initial values
$ declare -a ARRAY=(foo bar)             # automatic indexes (starting at zero)
$ declare -A MAP=([foo]=bar [baz]=qux)   # declare and set initial values
$ declare -A MAP=(foo bar baz qux)       # alternate syntax
$ declare -p ARRAY
declare -a ARRAY=([0]="foo" [1]="bar")
$ declare -p MAP
declare -A MAP=([foo]="bar" [baz]="qux" )

Retrieving

Individual values may be retrieved by referencing the variable with its subscript in the form ${ARRAY[subscript]}. The braces are required. All values may be retrieved by using @ or * as the subscript. All keys may be retrieved by also prefixing the array name with a !.

$ echo ${!ARRAY[@]}                      # all keys
10 20
$ echo ${ARRAY[@]}                       # all values
foo bar
$ echo ${ARRAY[10]}                      # individual element
foo
$ echo ${ARRAY[10*2]}                    # subscript is a arithmetic expression
bar
$ echo ${!MAP[@]}                        # all keys
foo baz
$ echo ${MAP[@]}                         # all values
bar qux
$ echo ${MAP[baz]}                       # individual element
qux

If there is no element at the subscript, a null string ("") is returned. This is also the case when referencing all keys or values and the array has no elements.

$ echo ${ARRAY[999]}

$ echo ${MAP[missing]}

$

When using set -u or set nounset to catch unset variables, these will generate an error. This can be avoided by adding a - to provide a default value if undefined.

$ set -u
$ echo ${ARRAY[999]}
-bash: ARRAY[999]: unbound variable
$ echo ${MAP[missing]}
-bash: MAP[missing]: unbound variable
$ echo ${ARRAY[999]-}

$ echo ${MAP[missing]-}

$

Subscripts for indexed arrays are, by default, zero-based monotonically increasing integers, but any integer may be used. Negative subscripts reference count back from the end of the array.

$ A=(first second third fourth)
$ echo ${A[1]}
second
$ echo ${A[-2]}
third
$ B=([10]=ten [20]=twenty [30]=thirty)
$ echo ${B[-11]}
twenty

Length

Length is specified as ${#ARRAY[subscript]. Using a subscript of @ or * returns the number of elements in the array, otherwise the length of that specific element of the array.

$ A=(first second third)
$ B=([10]=ten [20]=twenty [30]=thirty [40]=forty)
$ declare -A C=([one]=1 [two]=2)
$ echo ${#A[@]}
3
$ echo ${#B[@]}
4
$ echo ${#C[@]}                          # is the same with associative arrays
2
$ echo ${#B[40]}                         # "forty" is 5 characters long
5

Setting

Elements may be added to an array individually by specifying the index.

$ B=([10]=ten [20]=twenty [30]=thirty)
$ B[40]=forty
$ echo ${B[@]}
ten twenty thirty forty

The += operator appends to the existing list.

$ A=(first second)
$ A+=(third fourth)
$ echo ${A[@]}
first second third fourth
$ B=([10]=ten [20]=twenty)
$ B+=([30]=thirty [40]=forty)
$ echo ${B[@]}
ten twenty thirty forty

It is also possible to use the length as an index to add to the end of the array.

$ A=(first second)
$ A[${#A[@]}]=third                      # difficult to read
$ echo ${A[@]}
first second third

Sometimes, simply using value expansion is most straightforward.

$ A=(first second)
$ B=(third fourth)
$ declare -a C=(${A[@]} ${B[@]} fifth)
$ echo ${C[@]}
first second third fourth fifth

Deleting

The unset builtin can be used to delete individual elements or the whole array. A subscript of @ or * or not specified at all will delete the whole array, otherwise the specific element will be deleted.

$ declare -A MAP=([foo]=bar [baz]=qux)
$ echo ${MAP[@]}
bar qux
$ unset MAP[baz]                         # delete individual element
$ echo ${MAP[@]}
bar
$ unset MAP                              # delete whole array
$ echo ${MAP[@]}

$

Interpolation

$ B=([10]=ten [20]=twenty [30]=thirty [100]=hundred)
$ key=10
$ echo ${B[$key]}
ten
$ echo ${B[key]}                         # arithmetic expression, $ not required
ten
$ echo ${B[key**2]}
hundred
$ declare -A MAP=([foo]=bar [baz]=qux)
$ key=baz
$ echo ${MAP[$key]}
qux

Quoting

Quoting the subscript with single or double quotes is optional.

$ declare -A MAP
$ MAP[foo bar]=baz
$ MAP["foo bar"]=baz
$ MAP['foo bar']=baz
$ declare -p MAP
declare -A MAP=(["foo bar"]="baz" )

Slicing

Indexed arrays can be can use slice notation ${ARRAY:start:length} to return a subset of the array. start is the index of the first element to return and length is the count of elements to return. If length is unspecified all elements from the start to the end of the array are returned.

$ A=(one two three four five six seven)
$ echo ${A[@]:2:3}
three four five
$ echo ${A[@]:5}
six seven
$ B=([10]=ten [20]=twenty [30]=thirty [40]=forty)
$ echo ${B[@]:20:2}
twenty thirty

Looping

Often one wants to loop over all elements of an array.

$ B=([10]=ten [20]=twenty [30]=thirty [40]=forty)
$ for key in "${!B[@]}" ; do echo "$key is ${B[key]}" ; done
10 is ten
20 is twenty
30 is thirty
40 is forty