Skip to content
AgileLogics
6 min read

Arrays

Prerequisites: for loops, Quoting

An array holds a list of values in one variable — the safe way to keep lists of file names or arguments, even ones containing spaces.

Creating and reading arrays

fruits=("apple" "banana" "dragon fruit")

echo "${fruits[0]}"
echo "${fruits[2]}"
echo "${fruits[@]}"
echo "Count: ${#fruits[@]}"
echo "Last: ${fruits[-1]}"
apple
dragon fruit
apple banana dragon fruit
Count: 3
Last: dragon fruit
  • Indexes start at 0.
  • "${fruits[@]}" expands to every element; ${#fruits[@]} is the number of elements.
  • The braces are required: $fruits[0] doesn't work.

Looping safely

fruits=("apple" "banana" "dragon fruit")

for fruit in "${fruits[@]}"; do
  echo "- $fruit"
done
- apple
- banana
- dragon fruit

With the quotes, "dragon fruit" stays one element. Without them, it would split into two.

Changing arrays

files=()
files+=("report.pdf")
files+=("notes.txt" "photo 1.jpg")
files[0]="summary.pdf"
unset 'files[1]'
echo "${files[@]}"
echo "Indexes: ${!files[@]}"
echo "Slice: ${files[@]:0:1}"
summary.pdf photo 1.jpg
Indexes: 0 2
Slice: summary.pdf
  • +=(...) appends.
  • unset removes an element — leaving a gap in the indexes.
  • ${!files[@]} lists the indexes; ${files[@]:start:length} takes a slice.

Building command arguments

Arrays are the correct way to build up options for a command:

opts=(-l)
show_hidden=true
if [[ $show_hidden == true ]]; then
  opts+=(-a)
fi
echo "Would run: ls ${opts[*]}"
Would run: ls -l -a

In a real script you'd run ls "${opts[@]}", which passes each option as a separate argument.

Reading lines into an array

printf '%s\n' "first line" "second line" "third line" > lines.txt
mapfile -t lines < lines.txt
echo "${#lines[@]} lines; the second is: ${lines[1]}"
3 lines; the second is: second line

mapfile -t (also called readarray) reads each line into one element, removing the newlines.

Common mistakes

  • Forgetting quotes: ${fruits[@]} unquoted splits elements with spaces.
  • Storing a list in a plain string (files="a.txt b.txt") — it breaks on the first file name with a space. Use an array.