Bash arrays are ordered lists of strings, but they’re not just simple sequential indexes.
Let’s see one in action. Imagine you’re managing a list of server hostnames for a deployment script.
#!/bin/bash
servers=("web01.example.com" "db01.example.com" "app01.example.com")
echo "Total servers: ${#servers[@]}"
echo "First server: ${servers[0]}"
echo "All servers: ${servers[@]}"
# Add a new server
servers+=("cache01.example.com")
echo "Updated server list: ${servers[@]}"
# Iterate through servers
for server in "${servers[@]}"; do
echo "Processing: $server"
done
In this script, servers is a standard Bash array. servers=("web01.example.com" "db01.example.com" "app01.example.com") declares it. The [@] syntax is crucial; it expands the array into individual elements, preserving spaces within each element (which is important if your hostnames had them, though unlikely). ${#servers[@]} gives you the count, and ${servers[0]} accesses the first element (Bash arrays are zero-indexed). Appending is done with +=.
Associative arrays, on the other hand, are key-value pairs, much like dictionaries or hash maps in other languages. They don’t rely on numerical indices but on arbitrary strings as keys. This is incredibly useful when you need to map data points without a fixed order.
Here’s an example mapping server roles to their hostnames:
#!/bin/bash
declare -A roles
roles["web"]="web01.example.com"
roles["db"]="db01.example.com"
roles["app"]="app01.example.com"
echo "Database server: ${roles["db"]}"
echo "All roles: ${!roles[@]}"
echo "All hostnames: ${roles[@]}"
# Add a new role
roles["cache"]="cache01.example.com"
# Iterate through roles and hostnames
for role in "${!roles[@]}"; do
echo "Role: $role, Hostname: ${roles[$role]}"
done
The declare -A roles is what defines roles as an associative array. Accessing values uses square brackets with the key inside, like ${roles["db"]}. To get just the keys, you use ${!roles[@]}. To get just the values, it’s ${roles[@]}. Appending or modifying is as simple as assigning to a new or existing key.
The real power comes when you combine them or use them for more complex data structures. For instance, you might have an array of server objects, where each object is an associative array containing details like IP, role, and status.
#!/bin/bash
declare -A server1=(
[hostname]="web01.example.com"
[ip]="192.168.1.10"
[role]="web"
)
declare -A server2=(
[hostname]="db01.example.com"
[ip]="192.168.1.20"
[role]="db"
)
servers_array=("server1" "server2") # Array storing names of associative arrays
# Accessing data indirectly
server_name="server1"
echo "Hostname of ${server_name}: ${!server_name[hostname]}" # This syntax is incorrect for indirect access
# Correct way to access data from an array of associative arrays
# We need to store the associative arrays themselves, not just their names
declare -a server_objects
server_objects+=([0]=$(declare -p server1 | sed 's/declare -A server1=//'))
server_objects+=([1]=$(declare -p server2 | sed 's/declare -A server2=//'))
echo "Server 0 hostname: $(echo "${server_objects[0]}" | awk -F'[[:space:]]+' '{print $3}' | tr -d '"')"
echo "Server 1 IP: $(echo "${server_objects[1]}" | awk -F'[[:space:]]+' '{print $7}' | tr -d '"')"
# A more direct way if you manage the associative arrays directly
declare -A server_details
server_details["web01.example.com"]=$(declare -p server1 | sed 's/declare -A server1=//')
server_details["db01.example.com"]=$(declare -p server2 | sed 's/declare -A server2=//')
echo "Details for web01: ${server_details["web01.example.com"]}"
The declare -p command is key here, as it outputs the declaration of a variable, which you can then store and re-evaluate. Notice how the second example shows a more practical way to manage this: an associative array where keys are hostnames and values are the serialized declarations of the server detail associative arrays. This allows for quick lookups based on hostname.
When iterating through an associative array, the order you get the keys or values is not guaranteed. Bash versions prior to 4.0 did not support associative arrays at all, so ensure your bash executable is at least version 4.0 or higher. You can check with bash --version.
The most surprising true thing about Bash arrays is that even though they are conceptually ordered lists of strings, when you use "${array[@]}" or "${!array[@]}", Bash treats each element as a separate word after performing variable expansion. This means that if an element contains whitespace, it will be split into multiple words unless you quote it properly, like "${array[@]}".
The next concept you’ll likely run into is handling multidimensional arrays, which Bash doesn’t directly support but can be simulated with clever indexing and naming conventions.