Bash Arrays Explained

Written by: Linuxopsys   |   Last updated: April 10, 2023

The term array means a collection of items holding some information. This data is stored in an indexed manner. A Bash variable has the capacity to hold a single value. To hold multiple values at the same time, we have arrays. With arrays we can group a set of variables. Instead of creating a new variable every time, we can simply go ahead and use a single array variable that will store all the other variables. Bash arrays are unique as they can save different types of elements. 

Types of bash arrays

The bash arrays can be broadly categorized into numerically indexed arrays and associative arrays. 

Indexed arrays

Indexed arrays are arrays in which the elements are stored and assigned with an integer number starting from 0. The elements inside the array are referenced using the index number. 

Example

declare -a arr ("Europe" "Asia" "Australia")

Associative Array 

Associative arrays behave like a map and the values are made accessible using a key. The associative arrays have to be declared with the keyword with the -A flag. This is a must.

Example

declare -A array=(["Paris"]="France" ["Vienna"]="Austria" ["Oslo"]="Norway")

Difference  

The table lists the major differences between the two types of arrays:

Associative ArrayNumerically indexed array
Keys can be strings.Keys are whole numbers, starting from 0.
They act as maps.They are not maps.
They behave like two columns of a table. The first column is the key, which is used to access the second column, the value.They act as a single column table.
Used when we want to have key-value pairs.Used when we identify entries by their position.
The declare keyword is a must.The declare keyword is optional.
Referred to as dictionaries or hashtables.Referred to as lists.

Declare and initialize an array

There are different ways of declaring and initializing both the array types. 

Indexed arrays

The data in the array are referenced and accessed using numbers.

We can declare this type of array by using the declare keyword with the -a flag. 

declare -a arr

We can declare and initialize the array together.

declare -a arr=("Europe" "Asia" "Australia")

If we initialize the array, we can omit the declare keyword along with the -a flag.

arr=("Europe" "Asia" "Australia")

We have the option to use the indexes as follows:

arr=([0]="Europe" [1]="Asia" [2]="Australia")

We can split this further and assign values to the elements one by one.

arr[0]="Europe"
arr[1]="Asia"
arr[2]="Australia"

Associative Array 

Associative arrays behave like a map and the values are made accessible using a key. The associative arrays have to be declared with the keyword with the -A flag. This is a must.

declare -A array=(["Paris"]="France" ["Vienna"]="Austria" ["Oslo"]="Norway")

We can add more elements as follows:

array["Warsaw"]="Poland"
array["Helsinki"]="Finland"

Appending elements to an existing array

Given an array, we can append elements using the += operator. 

Syntax:

array+=(element1 elementN)

The += operator takes the array as the left operand and the elements as the right operand. The elements need to be enclosed in parenthesis. 

Example:

#!/bin/bash
arr=("Europe" "Asia" "Australia")
echo -n  "Current array  "
echo ${arr[@]}
arr+=("Antarctica" "Africa")
echo -n  "After appending 2 more elements it becomes "
echo ${arr[@]}
append to array

In this example, we have initialized an array with three string values and then appended two elements to this array. 

In associative arrays, too, we use the += operator to append one or more key-value pairs.

Example:

#!/bin/bash
declare -A arr
arr[Paris]=France
arr[Vienna]=Austria
echo -n  "Current array  "
echo ${arr[*]}
arr+=([Olso]=Norway [Warsaw]=Poland)
echo -n  "After appending 2 more elements it becomes "
echo ${arr[*]}
append to associative arrays

Refer and print array elements

Printing the array elements is pretty straight forward. 

#!/bin/bash
arr=("Europe" "Asia" "Australia")
arr+=("Antarctica" "Africa")
echo "First element is arr[0]"
echo "Second element is arr[1]"
echo "Fifth element is arr[4]"
echo "Array elements are " ${arr[@]}
echo "Array elements are " ${arr[*]}
print array elements

The elements are being referred to by their indexes. The first element is at index 0.

To refer to all the members of the array, we use the @ symbol. The use of the ∗ symbol as an index also gives the same output. The array. present inside the ${} construct, gets subjected to the parameter expansion.

Array Size

In Bash, there is no limit on the size of an array. The array members need not be indexed or assigned contiguously as well. Knowing the size of the array, we can iterate over all the elements using loops. 

echo ${#ArrayName[@]}
echo ${#ArrayName[*]}

The presence of @ or *expands all members of the array. Prefixing # to that array variable finds the number of elements present.

Is Array empty?

As we have learnt how to find the array size by using the # symbol, we can surround this inside an if-fi condition block.

Example:

#!/bin/bash

arr=("Europe" "Asia" "Australia")
if [[ ${#arr[@]} -ne 0 ]] 
then
    echo "Array is not empty"
else
    echo "Array is empty"
fi

empArr=()			#empty array
if [[ ${#empArr[@]} -ne 0 ]] 
then
    echo "Now array is not empty"
else
    echo "Now array is empty"
fi
check array empty

We just check and verify if the count of the array elements is zero. In the first case, we have a populated array. Therefore, it goes to the if block and prints "Array is not empty". However, in the second case, we have an empty array. This causes it to enter the else block and prints "Now array is empty".

Arrays with loop

We can iterate over the entire array using Bash loops. Bash provides us with three loops to achieve this. They are:

  • while
  • for
  • until

Let us put together everything we have learned so far in this example:

#!/bin/bash
arr=("Europe" "Asia" "Australia")	
arr+=("Antarctica" "Africa")

echo "--------- for loop over elements---------"
for i in ${arr[@]}			#Using for loop over elements
do
    echo "$i"
done

echo "--------- for loop with index---------"
for (( i=0; i<${#arr[@]}; i++ ))		#Using for loop with index
do
    echo ${arr[$i]}
done

echo "--------- while loop---------"
i=0
while [ $i -lt ${#arr[@]} ]		#Using while loop
do
    echo "${arr[$i]}"
    i=$((i + 1))
done

echo "--------- until loop---------"
i=0					#Using until loop
until [ $i -ge ${#arr[@]} ]
do
    echo "${arr[$i]}"
    i=$((i + 1))
done
array loop

We have used the parameter expansion with ${arr[@]} to return all the elements of the array. We can do the same with ${arr[*]}. After this, we just looped through the elements.

Let’s see how we can iterate over an associative array.

#!/bin/bash
declare -A arr=(["Paris"]="France" ["Vienna"]="Austria" ["Oslo"]="Norway")
for i in ${!arr[@]}
    do
        echo $i:${arr[$i]}		#key:value 
    done

There is no particular order in the output. It differs from the order of initialization. 

In index based arrays, we used ${#arr[@]} to retrieve the number of elements in the array. Index values are actually the keys in the map. Associative arrays return keys rather than indexes. Therefore, this incremental looping is not apt for maps. 

Create arrays on the fly

We cannot create an associative array on the fly in Bash. We have to use the declare built-in command with "-A". We can rely on the += operator which makes it possible to append one or more key-value pairs to the associative Bash array.

Example:

#!/bin/bash
declare -A arr
arr[Paris]=France
arr[Vienna]=Austria
arr+=([Olso]=Norway [Warsaw]=Poland)
echo ${arr[*]}
create arrays on fly

We can create indexed arrays on the fly. We don’t need to declare it explicitly.

Example:

#!/bin/bash
arr[0]="Europe" 
arr[1]="Asia" 
arr[2]="Australia"
arr[3]="Africa" 
echo ${arr[*]}
arr+=("Antarctica")
echo ${arr[*]}

Slicing an array

Slicing the array is to extract certain elements based on the position of their indexes. It is the same as extracting a substring from a string. With arrays we can pull out a particular number of elements from a given starting position.

Syntax:

${arr[@]:index:count}

Example:

#!/bin/bash
arr=("Europe" "Asia" "Australia" "Africa" "Antarctica")
echo ${arr[@]}
echo ${arr[@]:1:3}
echo ${arr[@]::3}
echo ${arr[@]:2}
slice array

In the first case, we are slicing the array in such a way that we are able to extract 3 elements from the first index.

By omitting the index and just providing only the count, it will slice from the 0th index till the specified length.

By omitting the count and only specifying the index, the elements from index position till the end of the array will be displayed 

Delete operations

In Bash, we can either delete the entire array in one go or remove a particular element. We can also put this inside a loop to delete elements one by one or as per the situation.

Remove elements from the array

To remove an element from the array, we use the unset command. Unsetting a variable instructs Bash to discard the variable from the list of variables for tracking. We cannot access the stored value in the variable once it is unset.

Example:

unset arr[2]

Delete the entire array

Just as we used the unset command to remove an element from the array, we can use the same to destroy the complete array.

Example:

unset arr

Clear the entire array

Sometimes we want to clear the contents of the complete array and keep it empty. To achieve this we just need to declare the array again. This way all the previous information in the array gets cleared. 

Example:

arr=()

Two-dimensional arrays

As we already know that an array is a collection of items. When these items are arrays as well, we get a two-dimensional (2D) array. A 2D array is like a matrix and the elements can be identified by their positions which are like coordinates. 

Bash does not support multi-dimensional arrays. As a result, elements of an array cannot be arrays. Bash only supports one-dimensional numerically indexed arrays as well as associative arrays. However, we can still implement them as follows:

Example:

#!/bin/bash
echo "------Associative array------"
declare -A arrA=([0,0]=Mercury [0,1]=Venus [1,0]=Earth [1,1]=Mars)	#Associative array
echo "${arrA[@]}"
for i in "${!arrA[@]}"
do 
    echo "$i -> ${arrA[$i]}"
done


echo "------Indexed array------"
NumRows=2
NumCols=2
declare -a arr=()			#declare 1D indexed array
arr+=(Mercury Venus)
arr+=(Earth Mars)


for (( row=0; row<$NumRows; row++ ))		
do
    for (( col=0; col<$NumCols; col++ ))		
    do
        pos=$(($NumCols*$row+$col))
        echo "$row,$col -> ${arr[$pos]}"
    done
done
Two-dimensional arrays

Since Bash doesn’t support 2D arrays, we can still implement them using the concept of 2D arrays and the other utilities Bash has on offer.

For 2D associative arrays, the indices used are simply strings acting as keys. We can generate the keys in a loop.

For numerical arrays, we define two variables- the number of rows and columns for the array. We then create 1D arrays and append data as we have learned earlier. Then in a nested loop we iterate over these variables. For every iteration, we get the position using the mathematical formula (NumCols*row+col).

Conclusion

  • Bash arrays are of two types- associative and indexed.
  • We have multiple ways of declaring and initializing an array.
  • We can create indexed arrays on the fly.
  • Using the unset command, we can delete the elements or the entire array itself.
  • Bash doesn’t support 2D arrays but we have learned how we can implement the same.
SHARE

Comments

Please add comments below to provide the author your ideas, appreciation and feedback.

Leave a Reply

Leave a Comment