Skip to content
Lesson 10 of 14

Step 1 of 5 · Reading · ~3 min

Learn

Arrays and Scripts

Arrays

Bash arrays are the language's late admission that word-splitting wasn't a data structure. They hold real lists — elements with spaces, empty elements, the works — if you use the (admittedly baroque) syntax exactly. The payoff: correct handling of filenames and arguments, the two lists every script actually manages.

The syntax, precisely

fruits=("apple" "kiwi fruit" "cherry")   # literal — note element 2 contains a space

fruits+=("mango")                        # append
echo "${fruits[0]}"                      # apple — index from 0, braces REQUIRED
echo "${#fruits[@]}"                     # 4 — length

Braces are non-negotiable: $fruits[0] without them means "$fruits then the literal text [0]" (bash expands $fruits as element 0 — a trap that almost works, the worst kind).

The one expansion to memorize: "${arr[@]}"

for f in "${fruits[@]}"; do
    echo "-> $f"
done

Output — four lines, "kiwi fruit" intact. That quoted-[@] form is the entire reason arrays exist: it expands to one word per element, boundaries preserved, no matter what the elements contain. Every variation loses:

  • ${fruits[@]} unquoted → word-splits: "kiwi fruit" becomes two items
  • "${fruits[*]}" → ONE word containing everything, space-joined

So: iterating, passing to commands, forwarding — always "${arr[@]}". (Its twin for script arguments is "$@", same semantics, which is why the Functions lesson called it the only correct forwarding form.)

Filling arrays from data

read -r -a nums                      # split ONE input line into an array

mapfile -t lines                     # slurp ALL of stdin, one element per LINE

files=(*.txt)                        # glob straight into an array — spaces safe

read -a for a line of fields, mapfile -t for lines (the -t trims newlines), glob-into-array for files. These three cover effectively every "get a list into bash" situation — and each replaces a word-splitting hack that breaks on real data.

Iterating with indices, when you must

for i in "${!nums[@]}"; do           # ${!arr[@]} = the list of indices
    echo "$i: ${nums[$i]}"
done

Rarely needed (element iteration covers most work), but ${!arr[@]} is the piece people can't find when they need position numbers.

String keys: associative arrays

Bash 4 added maps, and they need an explicit declaration:

declare -A ages
ages["ada"]=36
echo "${ages[ada]}"          # 36
for k in "${!ages[@]}"; do    # ${!arr[@]} gives the KEYS here
    echo "$k -> ${ages[$k]}"
done

The -A is load-bearing. Without it the name is an ordinary indexed array and the subscript is evaluated as arithmetic, so a non-numeric key quietly becomes index 0 and every key you set overwrites the same slot. Lowercase declare -a is the indexed kind.

Your exercise: Largest Number

Read a line of numbers and print the biggest -- the tracker pattern, with an array intake. The starter fills nums for you; the scan is yours.

Graded essentials, all old friends wearing brackets. Seed the running maximum from a real element rather than from 0, because the hidden case is all-negative and a zero seed would win it. Compare with (( )), where > means greater-than -- inside [[ ]] the same symbol would compare the numbers as text and rank 9 above 10. And iterate over the quoted "${nums[@]}", the expansion that keeps your list a list. The other classic miss is arithmetic: a maximum tracked in one variable and printed from another prints the seed.

Up nextScript HygieneArrays and Scripts

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…

Arrays — Bash Fundamentals