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.unsetremoves 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.