Introduction to Zsh Arrays
In the Z shell (Zsh), arrays are one of the most powerful and flexible data structures. Unlike simple strings or scalar variables, an array can hold an ordered list of elements — words, filenames, command output, or any combination of data. Zsh arrays are zero‑indexed, meaning the first element is at index 1 (unlike many programming languages that start at 0). They are also dynamic: you can add, remove, and modify elements without pre‑declaring a size.
This tutorial focuses on advanced array techniques. You will learn how to manipulate arrays using parameter expansion flags, perform slicing and merging, use associative arrays, and apply functional‑style operations like mapping and filtering. By the end, you will be able to write more concise, readable, and efficient Zsh scripts that leverage the full power of arrays.
Why Arrays Matter
In everyday shell scripting, you often need to process lists: file names, command arguments, configuration values, or output lines. Arrays let you:
- Store multiple values in a single variable without complex string splitting.
- Avoid word splitting and globbing issues that plague scalar strings.
- Iterate efficiently with
forloops or parameter expansion. - Transform data in place using Zsh’s built‑in operations (map, filter, reverse, unique, etc.).
- Write self‑documenting code that expresses intent clearly.
Mastering arrays is essential for any serious Zsh user – from interactive shell customisation to complex automation scripts.
Array Basics (Quick Recap)
🚀 Deploy your AI agent in 10 minutes
Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.
Try it free →Before diving into advanced techniques, here is a refresher on array fundamentals in Zsh:
# Define an array
my_array=(apple banana cherry)
# Access elements (index 1 = first)
echo $my_array[1] # apple
# Access all elements as separate words
echo "${my_array[@]}" # apple banana cherry
# Number of elements
echo $#my_array # 3
# Append an element
my_array+=("date")
# Remove an element (by index)
my_array[2]=() # removes banana
Notice the "${array[@]}" syntax: the double quotes preserve each element as a separate word, even if it contains spaces. This is a critical habit to adopt.
Advanced Array Operations
Parameter Expansion Flags
Zsh provides a rich set of parameter expansion flags that act directly on arrays. These flags are placed inside ${(flag)name} and can transform the array without external commands.
${(u)array}– remove duplicates (unique)${(o)array}– sort in ascending order${(O)array}– sort in descending order${(n)array}– sort numerically${(j:sep:)array}– join elements with a separator${(s:sep:)array}– split a scalar into an array using a separator${(k)assoc}– list keys of an associative array${(v)assoc}– list values of an associative array
Example: Unique and sorted list
colors=(red green blue red yellow green)
unique_sorted=(${(ou)colors})
echo "${unique_sorted[@]}" # blue green red yellow
Here (ou) applies o (sort) and u (unique) together. The order of flags matters; ou sorts first, then uniques. Using uo would unique then sort (same result in this case).
Slicing and Sub‑arrays
Zsh allows you to extract a slice of an array using index ranges. The syntax is ${array[start,end]}. Indices are 1‑based, and you can use negative indices to count from the end.
fruits=(apple banana cherry date elderberry fig)
echo "${fruits[2,4]}" # banana cherry date
echo "${fruits[-3,-1]}" # date elderberry fig
echo "${fruits[1,-2]}" # all except last element
You can also assign a slice to a new array:
first_three=("${fruits[1,3]}")
echo "${first_three[@]}" # apple banana cherry
Mapping and Filtering with ${array//pattern/replacement}
Parameter expansion supports global replacement on every element of an array when you use the / or // operators inside ${array/pattern/repl}.
names=("alice.txt" "bob.txt" "carol.csv")
# Remove extension from each element
basenames=("${names[@]/.txt/}")
echo "${basenames[@]}" # alice bob carol.csv
# Replace all 'o' with '0' in every element
obfuscated=("${names[@]//o/0}")
echo "${obfuscated[@]}" # alice.txt b0b.txt car0l.csv
This is a form of map. For filtering, you can combine with the (M) flag (match) or (R) flag (remove match). However, a more common approach is using the ${(M)array:#pattern} expansion.
files=("data.csv" "data.txt" "info.csv" "notes.txt")
# Keep only .txt files
txt_files=("${(M)files:#*.txt}")
echo "${txt_files[@]}" # data.txt notes.txt
The # pattern matches the entire element. (M) keeps matches; (R) removes them.
Associative Arrays (Hashes)
Zsh supports associative arrays (key‑value maps) since version 4.3. They are declared with typeset -A or local -A. Advanced techniques include iterating over keys/values, using parameter flags, and merging.
typeset -A capital
capital=(France Paris Germany Berlin Italy Rome)
# Access value by key
echo $capital[France] # Paris
# List all keys
echo "${(k)capital[@]}" # France Germany