HOWTO · Bash
Multi-Dimensional Array in Bash
Learn two ways to represent a two-dimensional table in Bash: a flat indexed array and an associative array.
On this page
Bash does not provide native nested or multidimensional arrays. Its arrays are one-dimensional, so a two-dimensional table needs a representation: either calculate a single index from each row and column, or use a string key that names both coordinates. Both approaches let you store, retrieve, traverse, and display table data.
The examples below use Bash syntax. Bash is available on systems beyond Linux as well, although the commands used to launch a script can differ by system. The first approach uses an indexed array and works well when the table has a fixed number of columns. The associative-array approach makes coordinates explicit, which can be easier to read when building keys dynamically. Confirm that your installed shell supports associative arrays before using that form; no numeric minimum version is assumed here.
Does Bash support multidimensional arrays?
No. An expression such as table[row][column] does not create a nested array in Bash. Instead, store each cell in a regular one-dimensional array and define how its coordinates map to an index. Alternatively, declare an associative array and construct a key from both coordinates. In either case, consistent key construction is what preserves the table structure.
Represent a 2D array with a flat indexed array
For a table with a fixed column count, map a zero-based row and column to the flat index row * columns + column. The inverse mapping uses integer division for the row and the remainder for the column. Keep the same column count whenever you calculate an index; changing it after storing values changes the meaning of every later position.
This example stores two names and ages in a two-row, two-column table. It calculates each name’s position and reads the adjacent cell for the age. The echo command is one way to print text in Bash; this example uses printf to keep the displayed fields together. It then changes Walker’s age in row 1, column 1, using the same formula, and reads the cell again.
Run this example:
rows=2
columns=2
cells=("Alen" "24" "Walker" "31")
for ((row = 0; row < rows; row++)); do
index=$((row * columns))
name=${cells[index]}
age=${cells[index + 1]}
printf 'Name: %s, Age: %s\n' "$name" "$age"
done
row=1
column=1
index=$((row * columns + column))
cells[index]="32"
printf 'Updated cell (%d, %d): %s\n' "$row" "$column" "${cells[index]}"
For a general cell, calculate its index with row * columns + column and assign the new value at that position. Traversal follows the stored order, so nested loops over row and column counts are useful when displaying a rectangular table. Keep loop bounds aligned with the dimensions: the last valid zero-based row is rows - 1, and the last column is columns - 1.
Represent a 2D array with an associative array
An associative array stores values under string keys rather than consecutive numeric indexes. Declare one with declare -A ArrayName, then use a composite key such as row,column. The comma separates the coordinates and makes the intended cell easy to recognize. Build the key in one consistent way for both assignments and lookups.
Here is a deterministic two-row, three-column table. The nested loops assign each cell and then traverse the same coordinates to print a labeled table. The values are fixed so readers can follow how the key is formed and retrieved.
declare -A table
rows=2
columns=3
table["0,0"]="Name"
table["0,1"]="Age"
table["0,2"]="City"
table["1,0"]="Alen"
table["1,1"]="24"
table["1,2"]="Delft"
for ((row = 0; row < rows; row++)); do
for ((column = 0; column < columns; column++)); do
key="$row,$column"
printf '%-8s' "${table[$key]}"
done
printf '\n'
done
The key must match exactly when reading a value. If a key is assembled differently during lookup, the requested cell will not be the one that was assigned. Use variables for the row and column, as above, instead of repeating a composite-key expression throughout a larger script. Associative arrays do not provide a natural table order, so use explicit row and column loops when the display order matters.
Choose a representation and avoid common pitfalls
Choose a flat indexed array when the column count is stable and compact storage is useful. Its index formula is simple, but readers of the code need to know the column count to interpret an index. Choose an associative array when explicit coordinate keys make the code easier to maintain or the table is sparse; the trade-off is that each lookup depends on constructing the same string key.
Neither representation is a native nested array. Keep dimensions and loop limits in sync, use one coordinate convention throughout, and avoid assuming that associative-array iteration will display cells in row order. When portability matters, check whether the target Bash supports declare -A rather than relying on an unsupported version claim. These examples demonstrate Bash array syntax and can be adapted to table values and dimensions suited to a script.