Code of the Day
AdvancedShell mastery

Arrays

Use Bash indexed arrays and associative arrays — creating, expanding, slicing, looping, and understanding when arrays beat space-separated strings.

BashAdvanced11 min read
Recommended first
By the end of this lesson you will be able to:
  • Create and access indexed arrays with arr=(...) and ${arr[@]}
  • Use ${#arr[@]} for length and array slices
  • Create associative arrays with declare -A and access by key
  • Loop over array keys and values
  • Explain why arrays are safer than space-separated strings

Beginner Bash stores lists in variables as space-separated strings. That works until a value contains a space — then word splitting silently corrupts the data. Arrays are the proper solution: each element is a discrete, quoted value, and the shell never splits on spaces within an element.

Indexed arrays

Create an indexed array with the =() syntax:

fruits=("apple" "banana" "cherry plum")

Access elements with ${arr[n]}, expand all with ${arr[@]}, and get the count with ${#arr[@]}:

echo "${fruits[0]}"        # apple
echo "${fruits[2]}"        # cherry plum (the space is preserved)
echo "${#fruits[@]}"       # 3

# All elements — always quote to preserve spaces
for f in "${fruits[@]}"; do
  echo "$f"
done

Append to an array:

fruits+=("date" "elderberry")
echo "${#fruits[@]}"   # 5

Assign or overwrite a specific index:

fruits[1]="blueberry"
echo "${fruits[1]}"    # blueberry

Unset a specific element:

unset 'fruits[2]'
# The array is now sparse — index 2 is gone, indices 0,1,3,4 remain
echo "${!fruits[@]}"   # 0 1 3 4 (the existing indices)

Array slices

${arr[@]:start:length} returns a sub-array:

nums=(10 20 30 40 50)
echo "${nums[@]:1:3}"    # 20 30 40  (starting at index 1, length 3)
echo "${nums[@]:2}"      # 30 40 50  (from index 2 to end)

This mirrors Python's list[1:4] — a useful mental bridge.

Printing indices

${!arr[@]} gives you the indices (keys), not the values:

for i in "${!fruits[@]}"; do
  echo "fruits[$i] = ${fruits[$i]}"
done

Why [@] and not [*]? Both expand all elements, but "${arr[@]}" (quoted, with @) preserves each element as a separate word. "${arr[*]}" joins all elements with the first character of IFS (usually a space) into a single string. For loops and function calls, always use "${arr[@]}".

Associative arrays

Declare an associative array with declare -A. Keys are arbitrary strings:

declare -A config
config["host"]="localhost"
config["port"]="5432"
config["user"]="admin"

echo "${config["host"]}"       # localhost
echo "${#config[@]}"           # 3 (number of keys)

Iterate over key-value pairs:

for key in "${!config[@]}"; do
  echo "$key = ${config[$key]}"
done

Associative arrays require Bash 4+. macOS ships with Bash 3.2 (due to licence reasons), which does not support declare -A. Check with bash --version. If you need to support macOS users with the system Bash, use a flat string encoding or require Homebrew Bash 5.

Check your understanding

  1. 1.
    arr=("file one" "file two"). Which expansion passes both elements correctly to a command that expects two arguments?
  2. 2.
    What must you do before using an associative array in Bash?
  3. 3.
    After unset arr[1] on a 5-element array, ${#arr[@]} returns 4.

Do it yourself

# Indexed array: files with spaces
declare -a myfiles=("/tmp/file one.txt" "/tmp/file two.txt")
for f in "${myfiles[@]}"; do echo "File: $f"; done

# Slice
nums=(10 20 30 40 50 60)
echo "Middle three: ${nums[@]:1:3}"

# Associative array: simple config store
declare -A cfg
cfg[host]="localhost"
cfg[port]="8080"
for k in "${!cfg[@]}"; do echo "$k=${cfg[$k]}"; done

# Append and check length
myfiles+=("/tmp/file three.txt")
echo "Now ${#myfiles[@]} files"

Where to go next

Arrays give you proper data structures in the shell. The next lesson — — shows the full set of ${...} forms for defaults, string manipulation, prefix/suffix stripping, and case conversion.

Finished reading? Mark it complete to track your progress.

On this page