Skip to content
Lesson 10 of 14

Step 1 of 5 · Reading · ~3 min

Learn

Arrays and Scripts

Arrays

By the end of this lesson you can keep a real list in bash, loop over it without breaking elements that contain spaces, and build a lookup table keyed by text. Bash arrays are the language's answer to the fact that word-splitting is not a data structure. They hold elements with spaces, empty elements, and anything else, as long as you use the syntax exactly.

Indexed arrays

tools=("git" "make tool" "curl")     # the second element contains a space

tools+=("jq")                        # append
echo "${tools[0]}"                   # git      (index from 0, braces required)
echo "${#tools[@]}"                  # 4        (number of elements)
tools[1]="cmake"                     # replace one element

Braces are not optional: $tools[0] means "$tools, then the literal text [0]". Because $tools alone expands to element 0, the mistake almost works, which is the worst kind.

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

for t in "${tools[@]}"; do
    echo "-> $t"
done

The quoted [@] form expands to one word per element, boundaries preserved, whatever the elements contain. That is the entire reason arrays exist. Every variation loses something:

  • ${tools[@]} unquoted splits again, so make tool becomes two items.
  • "${tools[*]}" joins everything into one word.

So when you iterate, pass a list to a command, or forward it, write "${arr[@]}". Its twin for a script's own arguments is "$@".

Filling arrays from data

read -r -a parts          # split ONE input line into an array of words
mapfile -t lines          # read ALL of stdin, one element per line
files=(*.log)             # a glob straight into an array, spaces safe

read -a is for a line of fields, mapfile -t (the -t strips the newlines) for a list of lines, and a glob for files. Each replaces a word-splitting hack that breaks on real data. Indices are available too: "${!tools[@]}" expands to 0 1 2 3, which matters when you need positions.

Associative arrays: text keys

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

declare -A capital
capital[france]="Paris"
capital[japan]="Tokyo"

echo "${capital[japan]}"                 # Tokyo
echo "${#capital[@]}"                    # 2 entries
echo "${capital[peru]:-unknown}"         # unknown  (:- supplies a default)

if [[ -v capital[france] ]]; then        # -v: does this key exist?
    echo "known"
fi

unset 'capital[france]'                  # delete one entry

You can fill one in a single statement with declare -A stock=([apples]=5 [pears]=0). To loop over the entries, "${!capital[@]}" expands to the keys, and ${capital[$key]} gives each value. The order of the keys is not the order you inserted them and is not guaranteed to be stable, so when order matters, keep the keys in an indexed array as you go.

Inside (( )) an entry that does not exist counts as 0, so (( stock[apples] += 5 )) works even for a brand-new key.

The -A is load-bearing. Without it the name is an ordinary indexed array and the subscript is evaluated as arithmetic: a text key such as france is read as a variable name, which is unset and means 0. Every key then silently lands in slot 0 and overwrites the last one.

Common mistakes

  • Unquoted ${arr[@]}. Elements containing spaces fall apart into several words.
  • ${#arr} for the length. That is the length of element 0. The element count is ${#arr[@]}.
  • Forgetting declare -A. Text keys all collapse into index 0, and the "map" keeps only the last value written.
  • Relying on key order. Iterating "${!map[@]}" gives keys in an unspecified order; track the order yourself if the output depends on it.
  • A missing key read as empty. ${map[k]} is an empty string for an absent key, not an error. Test with -v, or give a default with :-.

Your exercise

Count how often each word occurs in a line, and report the words in the order they first appeared. You need a structure that answers "have I seen this word, and how many times?" and a second one that remembers the order. The hidden tests include words that are all different from each other and words that repeat across the whole line.

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…