`ARRAY[N]=VALUE` Sets the element `N` of the **indexed** array `ARRAY` to `VALUE`. **`N` can be any valid [arithmetic expression](../syntax/arith_expr.md)**.
`ARRAY[STRING]=VALUE` Sets the element indexed by `STRING` of the **associative array**`ARRAY`.
`ARRAY=VALUE` As above. If no index is given, as a default the zeroth element is set to `VALUE`. Careful, this is even true of associative arrays - there is no error if no key is specified, and the value is assigned to string index \"0\".
`ARRAY=(E1\ E2\ ...)` Compound array assignment - sets the whole array `ARRAY` to the given list of elements indexed sequentially starting at zero. The array is unset before assignment unless the += operator is used. When the list is empty (`ARRAY=()`), the array will be set to an empty array. This method obviously does not use explicit indexes. An **associative array** can **not** be set like that! Clearing an associative array using `ARRAY=()` works.
`ARRAY=([X]=E1\ [Y]=E2\ ...)` Compound assignment for indexed arrays with index-value pairs declared individually (here for example `X` and `Y`). X and Y are arithmetic expressions. This syntax can be combined with the above - elements declared without an explicitly specified index are assigned sequentially starting at either the last element with an explicit index, or zero.
`ARRAY=([S1]=E1\ [S2]=E2\ ...)` Individual mass-setting for **associative arrays**. The named indexes (here: `S1` and `S2`) are strings.
`ARRAY+=(E1\ E2\ ...)` Append to ARRAY.
`ARRAY=("${ANOTHER_ARRAY[@]}")` Copy ANOTHER_ARRAY to ARRAY, copying each element.
As of now, arrays can\'t be exported.
### Getting values
\<note\> For completeness and details on several parameter expansion
`${ARRAY[N]}` Expands to the value of the index `N` in the **indexed** array `ARRAY`. If `N` is a negative number, it's treated as the offset from the maximum assigned index (can\'t be used for assignment) - 1
`"${ARRAY[@]}" ${ARRAY[@]} "${ARRAY[*]}" ${ARRAY[*]}` Similar to [mass-expanding positional parameters](../scripting/posparams.md#mass_usage), this expands to all elements. If unquoted, both subscripts `*` and `@` expand to the same result, if quoted, `@` expands to all elements individually quoted, `*` expands to all elements quoted as a whole.
`"${ARRAY[@]:N:M}" ${ARRAY[@]:N:M} "${ARRAY[*]:N:M}" ${ARRAY[*]:N:M}` Similar to what this syntax does for the characters of a single string when doing [substring expansion](../syntax/pe.md#substring_expansion), this expands to `M` elements starting with element `N`. This way you can mass-expand individual indexes. The rules for quoting and the subscripts `*` and `@` are the same as above for the other mass-expansions.
`a[0]` is assigned to the result of `2+4`. `a[2]` gets the result of
`2+2`. The last index in the first assignment is the result of `a[2]`,
which has already been assigned as `4`, and its value is also given
`a[2]`.
This shows that even though any existing arrays named `a` in the current
scope have already been unset by using `=` instead of `+=` to the
compound assignment, arithmetic variables within keys can self-reference
any elements already assigned within the same compound-assignment. With
integer arrays this also applies to expressions to the right of the `=`.
(See [evaluation order](#evaluation_order), the right side of an
arithmetic assignment is typically evaluated first in Bash.)
The second compound assignment argument to declare uses `+=`, so it
appends after the last element of the existing array rather than
deleting it and creating a new array, so `a[5]` gets `42`.
Lastly, the element whose index is the value of `a[4]` (`4`), gets `3`
added to its existing value, making `a[4]` == `7`. Note that having the
integer attribute set this time causes += to add, rather than append a
string, as it would for a non-integer array.
The single quotes force the assignments to be evaluated in the
environment of `declare`. This is important because attributes are only
applied to the assignment after assignment arguments are processed.
Without them the `+=` compound assignment would have been invalid, and
strings would have been inserted into the integer array without
evaluating the arithmetic. A special-case of this is shown in the next
section.
\<note\> Bash declaration commands are really keywords in disguise. They
magically parse arguments to determine whether they are in the form of a
valid assignment. If so, they are evaluated as assignments. If not, they
are undergo normal argument expansion before being passed to the builtin
which evaluates the resulting string as an assignment (somewhat like
`eval`, but there are differences.) `'Todo:`\' Discuss this in detail.
\</note\>
### Indirection
Arrays can be expanded indirectly using the indirect parameter expansion
syntax. Parameters whose values are of the form: `name[index]`,
`name[@]`, or `name[*]` when expanded indirectly produce the expected
results. This is mainly useful for passing arrays (especially multiple
arrays) by name to a function.
This example is an \"isSubset\"-like predicate which returns true if all
key-value pairs of the array given as the first argument to isSubset
correspond to a key-value of the array given as the second argument. It
demonstrates both indirect array expansion and indirect key-passing
without eval using the aforementioned special compound assignment
expansion.
isSubset() {
local -a 'xkeys=("${!'"$1"'[@]}")' 'ykeys=("${!'"$2"'[@]}")'
set -- "${@/%/[key]}"
(( ${#xkeys[@]} <= ${#ykeys[@]} )) || return 1
local key
for key in "${xkeys[@]}"; do
[[ ${!2+_} && ${!1} == ${!2} ]] || return 1
done
}
main() {
# "a" is a subset of "b"
local -a 'a=({0..5})' 'b=({0..10})'
isSubset a b
echo $? # true
# "a" contains a key not in "b"
local -a 'a=([5]=5 {6..11})' 'b=({0..10})'
isSubset a b
echo $? # false
# "a" contains an element whose value != the corresponding member of "b"
local -a 'a=([5]=5 6 8 9 10)' 'b=({0..10})'
isSubset a b
echo $? # false
}
main
This script is one way of implementing a crude multidimensional
associative array by storing array definitions in an array and
referencing them through indirection. The script takes two keys and
dynamically calls a function whose name is resolved from the array.
callFuncs() {
# Set up indirect references as positional parameters to minimize local name collisions.
set -- "${@:1:3}" ${2+'a["$1"]' "$1"'["$2"]'}
# The only way to test for set but null parameters is unfortunately to test each individually.
local x
for x; do
[[ $x ]] || return 0
done
local -A a=(
[foo]='([r]=f [s]=g [t]=h)'
[bar]='([u]=i [v]=j [w]=k)'
[baz]='([x]=l [y]=m [z]=n)'
) ${4+${a["$1"]+"${1}=${!3}"}} # For example, if "$1" is "bar" then define a new array: bar=([u]=i [v]=j [w]=k)
${4+${a["$1"]+"${!4-:}"}} # Now just lookup the new array. for inputs: "bar" "v", the function named "j" will be called, which prints "j" to stdout.
}
main() {
# Define functions named {f..n} which just print their own names.
local fun='() { echo "$FUNCNAME"; }' x
for x in {f..n}; do
eval "${x}${fun}"
done
callFuncs "$@"
}
main "$@"
## Bugs and Portability Considerations
- Arrays are not specified by POSIX. One-dimensional indexed arrays
are supported using similar syntax and semantics by most Korn-like
shells.
- Associative arrays are supported via `typeset -A` in Bash 4, Zsh,
and Ksh93.
- In Ksh93, arrays whose types are not given explicitly are not
necessarily indexed. Arrays defined using compound assignments which
specify subscripts are associative by default. In Bash, associative
arrays can *only* be created by explicitly declaring them as
associative, otherwise they are always indexed. In addition, ksh93
has several other compound structures whose types can be determined
by the compound assignment syntax used to create them.
- In Ksh93, using the `=` compound assignment operator unsets the
array, including any attributes that have been set on the array
prior to assignment. In order to preserve attributes, you must use
the `+=` operator. However, declaring an associative array, then
attempting an `a=(...)` style compound assignment without specifying
indexes is an error. I can\'t explain this
inconsistency.` $ ksh -c 'function f { typeset -a a; a=([0]=foo [1]=bar); typeset -p a; }; f' # Attribute is lost, and since subscripts are given, we default to associative.
typeset -A a=([0]=foo [1]=bar)
$ ksh -c 'function f { typeset -a a; a+=([0]=foo [1]=bar); typeset -p a; }; f' # Now using += gives us the expected results.
typeset -a a=(foo bar)
$ ksh -c 'function f { typeset -A a; a=(foo bar); typeset -p a; }; f' # On top of that, the reverse does NOT unset the attribute. No idea why.
ksh: f: line 1: cannot append index array to associative array a
`
- Only Bash and mksh support compound assignment with mixed explicit
subscripts and automatically incrementing subscripts. In ksh93, in
order to specify individual subscripts within a compound assignment,
all subscripts must be given (or none). Zsh doesn\'t support
specifying individual subscripts at all.
- Appending to a compound assignment is a fairly portable way to
append elements after the last index of an array. In Bash, this also
sets append mode for all individual assignments within the compound
assignment, such that if a lower subscript is specified, subsequent
elements will be appended to previous values. In ksh93, it causes
subscripts to be ignored, forcing appending everything after the
last element. (Appending has different meaning due to support for
multi-dimensional arrays and nested compound datastructures.)
` $ ksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=([3]=blah [0]=bork [1]=blarg [2]=zooj); typeset -p a; }; f' # ksh93 forces appending to the array, disregarding subscripts
typeset -a a=(foo bar baz '[3]=blah' '[0]=bork' '[1]=blarg' '[2]=zooj')
$ bash -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Bash applies += to every individual subscript.
declare -a a='([0]="foobork" [1]="barblarg" [2]="bazzooj" [3]="blah")'
$ mksh -c 'function f { typeset -a a; a+=(foo bar baz); a+=(blah [0]=bork blarg zooj); typeset -p a; }; f' # Mksh does like Bash, but clobbers previous values rather than appending.
set -A a
typeset a[0]=bork
typeset a[1]=blarg
typeset a[2]=zooj
typeset a[3]=blah
`
- In Bash and Zsh, the alternate value assignment parameter expansion
(`${arr[idx]:=foo}`) evaluates the subscript twice, first to
determine whether to expand the alternate, and second to determine
the index to assign the alternate to. See [evaluation
order](#evaluation_order).
` $ : ${_[$(echo $RANDOM >&2)1]:=$(echo hi >&2)}
13574
hi
14485
`
- In Zsh, arrays are indexed starting at 1 in its default mode.
Emulation modes are required in order to get any kind of
portability.
- Zsh and mksh do not support compound assignment arguments to
`typeset`.
- Ksh88 didn\'t support modern compound array assignment syntax. The
original (and most portable) way to assign multiple elements is to
use the `set -A name arg1 arg2 ...` syntax. This is supported by
almost all shells that support ksh-like arrays except for Bash.
Additionally, these shells usually support an optional `-s` argument
to `set` which performs lexicographic sorting on either array
elements or the positional parameters. Bash has no built-in sorting
ability other than the usual comparison operators.
` $ ksh -c 'set -A arr -- foo bar bork baz; typeset -p arr' # Classic array assignment syntax