Declaring and Populating Indexed Arrays
Bash indexed arrays are declared implicitly by assignment, e.g. fruits=(apple banana cherry), or explicitly with declare -a; indices are not required to be contiguous, so you can assign fruits[10]=mango and leave gaps, since Bash arrays are technically sparse. Elements are accessed with ${array[index]}, the full array with ${array[@]} or ${array[*]}, and new elements are appended safely with array+=(newitem) rather than manually tracking the next index.
Cricket analogy: A batting order lineup declared as batsmen=(Rohit Gill Kohli Rahul) mirrors an indexed array's initial population, and a sparse array with gaps is like a squad list where jersey numbers 4 and 18 are reserved but currently unassigned to any player.
#!/usr/bin/env bash
set -euo pipefail
# Implicit declaration
fruits=(apple banana cherry)
# Explicit declaration
declare -a scores
scores=(90 85 78)
# Sparse assignment
fruits[10]="mango"
echo "${fruits[1]}" # banana
echo "${#fruits[@]}" # 4 (element COUNT, not highest index)
echo "${!fruits[@]}" # 0 1 2 10 (the actual indices present)
# Safe append
fruits+=("dragonfruit")
echo "${fruits[@]}"
@ vs * Expansion and Word Splitting
"${array[@]}" expands each element as a separate, individually-quoted word — the only correct way to iterate over an array whose elements may contain spaces — whereas "${array[*]}" joins all elements into a single string separated by the first character of $IFS, which is almost never what you want in a for loop. Dropping the quotes on either form reintroduces word splitting and glob expansion on every element, defeating the purpose of using an array in the first place.
Cricket analogy: "${array[@]}" is like calling each fielder onto the ground individually by name for a team photo, each standing distinctly, while "${array[*]}" is like squeezing the whole team into one blurred group huddle where individual players are no longer distinguishable.
Always iterate with for item in "${array[@]}" (quoted, with @). Using "${array[*]}" in a for loop, or leaving either form unquoted, is one of the most common Bash array bugs — it silently breaks on elements containing spaces and can turn one array element into several loop iterations or vice versa.
Slicing, Deleting, and Common Operations
Array slicing with ${array[@]:offset:length} extracts a subset of elements starting at offset for length elements, mirroring substring slicing syntax on strings; unset array[index] removes a single element (leaving a gap in the indices, since arrays are sparse) while unset array removes the entire array. Passing an array to a function requires either using a nameref (local -n ref=$1) in Bash 4.3+ or explicitly re-expanding "$@" after positional-parameter assignment, since Bash cannot pass arrays by value as a single function argument the way scalar variables are passed.
Cricket analogy: ${array[@]:2:3} pulling three players starting from the third batting position mirrors slicing a specific stretch of the batting order for a middle-overs analysis, without touching the rest of the lineup.
servers=(web1 web2 db1 db2 cache1 cache2)
# Slicing: offset:length
echo "${servers[@]:2:2}" # db1 db2
# Removing one element (leaves a sparse gap)
unset 'servers[0]'
echo "${servers[@]}" # web2 db1 db2 cache1 cache2
# Passing an array to a function via nameref (Bash 4.3+)
summarize() {
local -n ref=$1
echo "Count: ${#ref[@]}, First: ${ref[0]:-none}"
}
summarize servers
- Bash indexed arrays are sparse: indices need not be contiguous, and ${#array[@]} counts elements present, not the highest index.
- Declare arrays implicitly with name=(a b c) or explicitly with declare -a; append safely with array+=(item).
- Always use "${array[@]}" (quoted, @) to iterate — it preserves each element as a separate word even with embedded spaces.
- "${array[*]}" joins all elements into one string using $IFS's first character, which is rarely what you want.
- ${array[@]:offset:length} slices a subrange of elements, mirroring string substring syntax.
- unset 'array[i]' removes a single element and leaves a gap; unset array removes the whole array.
- Pass arrays into functions using a nameref (local -n ref=$1) since Bash cannot pass arrays by value as a single argument.
Practice what you learned
1. Given arr=(a b c); arr[10]=z, what does ${#arr[@]} return?
2. Why should you use "${array[@]}" rather than "${array[*]}" when iterating in a for loop?
3. What is the correct, idiomatic way to append an item to an existing array in Bash?
4. What does ${servers[@]:2:2} return for servers=(web1 web2 db1 db2 cache1)?
5. How should an array be passed into a function so the function can modify the caller's original array (Bash 4.3+)?
Was this page helpful?
You May Also Like
Advanced Parameter Expansion
Master Bash's ${...} parameter expansion operators for defaults, substring removal, pattern substitution, case conversion, and length/indirection tricks that eliminate the need for external tools like sed and awk in many scripts.
Associative Arrays
Bash 4+ associative arrays bring key-value maps to shell scripting — learn declaration, iteration order, key existence checks, and real-world patterns like counters and lookup tables.
Review of Bash Basics
A refresher on core Bash concepts—shell startup, variables, quoting, and control flow—that underpin the advanced scripting techniques covered later in this course.
Related Reading
Related Study Notes in DevOps
Browse all study notesNginx Study Notes
DevOps · 30 topics
DevOpsAnsible Study Notes
DevOps · 30 topics
DevOpsAdvanced Kubernetes Study Notes
Kubernetes · 30 topics
DevOpsApache Kafka Study Notes
Kafka · 30 topics
DevOpsDocker & Kubernetes Study Notes
YAML · 40 topics
DevOpsCI/CD Tools & Pipelines Study Notes
YAML · 37 topics