Bash supports both indexed arrays and associative arrays (dictionaries). Indexed arrays are declared with arr=(one two three) or by element with arr[0]=one, and individual elements are accessed with \({arr[0]}. All elements are accessed with \){arr[@]} (each as a separate word) or \({arr[*]} (joined into one word), and the element count is \){#arr[@]}. New elements are appended with arr+=(four five). Iteration over indices and values together uses \({!arr[@]} to enumerate the indices: for i in "\){!arr[@]}"; do echo "\(i=\){arr[i]}"; done. Associative arrays require Bash 4 and are declared with declare -A m=([key1]=val1 [key2]=val2); keys come from \({!m[@]} and the length is \){#m[@]}.
The expansions covered in the variables chapter apply naturally to strings held in scalar variables and elements of arrays. Substrings are extracted with ${var:offset:length}, prefixes and suffixes are removed with #, ##, %, and %%, and replacement uses / for the first match, // for all, and the # and % anchors for the start and end. Case conversion uses \({var^^} and \){var,,}. To join an array into a single delimited string, set IFS locally and use "\({arr[*]}", or pipe through printf '%s\n' "\){arr[@]}" | paste -sd , for arbitrary joining.
Arithmetic in Bash is handled by the $(( ... )) expansion, which supports the usual operators: +, -, *, /, %, ** for power, bitwise operators, and the ternary. Inside (( ... )) you can use C-style syntax and assign back to variables, as in ((count++)), and the command form returns 0 when the expression's result is non-zero, making if (( a < b )); then ...; fi a natural integer test. To safely coerce a string to an integer while refusing non-digits, use base-10 notation: n=\(((10#\)s)) errors on trailing garbage instead of silently treating it as octal. All Bash arithmetic is integer-only; for floating-point comparison you must fall back to awk or bc, e.g. awk -v a=1.5 -v b=1.4 'BEGIN{exit !(a>b)}'.
Two substitution forms deserve their own mention. Command substitution — using a command's output as a value — uses $(command), the modern and easily nestable form, rather than the legacy backticks `command` (which nest poorly and behave inconsistently inside double quotes). Process substitution goes further, exposing a command's input or output as a filename referring to a /dev/fd/N pipe: diff <(sort a) <(sort b) is the classic example, replacing temporary files with on-the-fly pipes.