The day a script needs a list — servers, files, options — bash arrays arrive. They are bash's real data structure, and most of their reputation for awkwardness comes from using them the way you would use a string. Handled with "${arr[@]}", they are dependable.
Creating and reading #
$ servers=(web1 web2 db1)
$ echo "${servers[0]}"
web1
$ servers+=(db2)
$ echo "${#servers[@]}"
4
Parentheses make the array; spaces separate elements — so elements with spaces arrive intact, no escaping gymnastics. Appending is +=(...), counting is ${#arr[@]}.
The one true iteration #
$ for s in "${servers[@]}"; do ping -c1 "$s"; done
The quotes around "${servers[@]}" are the whole note: quoted, each element stays one word even with spaces; unquoted, the array explodes back into a whitespace-split string. The same law governs "${@}" for script arguments — same machinery.
Slices and keys #
| Form | Result |
|---|---|
${arr[@]:1:2} | Elements 1 and 2 (offset, length) |
${arr[-1]} | Last element |
${!arr[@]} | All indices — or keys, for associative |
${arr[@]/*.old/*.new} | Pattern-replaced copy |
unset 'arr[2]' | Remove one element (index stays sparse) |
Associative arrays — declared with declare -A — map keys to values, and quietly replace a lot of case-statements:
declare -A env_url=(
[dev]="https://dev.aospix.com"
[prod]="https://aospix.com"
)
echo "${env_url[$environment]:-unknown env}"
The :- default keeps unknown keys from expanding to empty — the strict-mode friendly form.
Where arrays beat strings #
Any time a list is passed around — building a command with optional flags, collecting failed hosts, holding a find result — an array beats a space-joined string because it never re-splits:
args=(-a --delete)
[[ $verbose == 1 ]] && args+=(-v)
rsync "${args[@]}" src/ dst/
Elements and expansion share the quoting law; building commands from arrays pairs with xargs for the argument-heavy cases. The scripting series continues.
