How to Write Bash Scripts to Loop Through Array Values

Looping through array values is a common task in Bash scripting. For instance, you might want to loop through an array of filenames and execute specific commands on each one. In this blog post, you’ll learn how to write bash scripts that utilize for loops to iterate through array values.

While for and while loops are often interchangeable in Bash, we'll be focusing solely on for loops. Why? For loops offer a more streamlined syntax, leading to less verbose code compared to their while loop counterparts.

Before we jump into scripting, let’s refresh our understanding of the two types of arrays in Bash - indexed and associative. Additionally, we'll also revisit the two common syntaxes of for loops - the traditional Bash syntax and the C-style syntax. Let’s get started!

Bash Array Types

In Bash, there are two types of arrays: indexed arrays and associative arrays.

Indexed Array

An indexed array is a type of array where each element is assigned an automatic index based on its position. The first element in an array has an index of 0, the second has an index of 1, and so forth.

To define an indexed array, you use the following syntax:

array_name=("element1" "element2" "element3" ...)

Associative Array

An associative array is an array that uses keys instead of numerical indices to store and access data.

To define an associative array in Bash, you use the declare command (declare -A), followed by the array name and its key-value pairs:

declare -A array_name=(["key1"]="value1" ["key2"]="value2" ...)

Bash For Loop Syntax

The for loop in Bash has two syntax forms: the traditional Bash syntax and a C-style syntax.

Bash Syntax

The traditional Bash for loop syntax is a simple and commonly used form. It loops through a list of items and performs a set of commands for each item. The list of items can be a series of strings, numbers, or elements of an array. Here's what the Bash syntax looks like: 

for item in item_list
do
   commands
done

In this syntax, item is a variable that represents the current item in the item_list for each iteration of the loop. The commands are the actions to be performed on each item.

C-style Syntax

Bash also supports a C programming language style syntax for the for loop. This syntax is particularly useful when you need to perform an action a specific number of times. The C-style syntax looks like this:

for (( initialization; condition; increment ))
do
   commands
done

In the C-style for loop, initialization is executed once before the loop begins. The condition is evaluated before each iteration of the loop, and if it evaluates to true, the loop continues; if not, the loop ends. The increment is executed at the end of each loop iteration. The commands within the loop are executed for each iteration.

For instance, when iterating over an array using a C-style for loop, you'd typically initialize an index variable to 0, continue the loop while the index is less than the array length, and increment the index at the end of each iteration.

Now that we've covered the basics of different Bash array types and for loop syntax, let's dive in and write some Bash scripts to loop through array values.

Prerequisites

To try out the scripts in this blog post, you need access to a Bash shell. You also need a text editor, such as "nano" or "vim", which come pre-installed by default in many Unix-like operating systems.

For the purpose of this blog post, I'll be using KodeKloud’s Ubuntu playground, which lets you access a pre-installed Ubuntu operating system in just one click. Best of all, you won't need to go through the hassle of installing any additional software— everything you need is already set up and ready to use.

Bash Script for Looping Through Indexed Arrays With a For Loop

Let’s start by creating a new script file. Run the following command to create the demo.sh file in the /usr/local/bin directory (or any other location you prefer) and open it in the "nano" text editor:

nano /usr/local/bin/demo.sh

You should see the "nano" text editor in your terminal, as shown below:

Next, insert the following script:

#!/bin/bash

# Define your array
countries=("India" "France" "United Kingdom")

# Loop through the array
for country in "${countries[@]}"
do
   echo "$country"
done

In this script, we first define an array named countries with three values. Then, we use a for loop to iterate over each value in the array. The "${countries[@]}" (known as array expansion) syntax is used to represent all elements of the array. The echo "$country" command prints each element of the array to the console.

After you've finished writing your script, press ctrl + o to save the changes, and then ctrl + x to close the editor.

Now, execute the script by running the following command:

bash demo.sh

Note: The demo.sh script is located in /usr/local/bin, a directory included in the system's PATH variable. Therefore, when executing bash demo.sh, we don't need to specify the absolute or relative path to demo.sh—the system knows where to find it. Furthermore, we don't have to make demo.sh executable because we're directly invoking the bash interpreter to interpret and execute the script.

After running the command, you should see the elements of the countries array printed to your terminal on separate lines like this:

Accessing array elements and their indices

Sometimes, in addition to the elements of an array, you may also want to access their respective indices. Bash provides a convenient way to do this using the ! symbol in array expansions. Replace the script in the demo.sh file with the following script:

#!/bin/bash

# Define your array
countries=("India" "France" "United Kingdom")

# Loop through the array
for index in "${!countries[@]}"
do
   echo "Index: $index, Value: ${countries[$index]}"
done

In the script above, note the use of an exclamation point (!) immediately before our array variable within the for loop declaration. This symbol signals Bash to iterate over the indices of the array rather than its values.

In this modified script, "${!countries[@]}" generates all the indices of the countries array. The loop then iterates over these indices. During each iteration, index holds the current index, and ${countries[$index]} retrieves the value at that index. Thus, the statement echo "Index: $index, Value: ${countries[$index]}" prints both the index and its corresponding value for each iteration.

Now run the script. You should see both the index and its corresponding value, as shown below:

Using C-style for loop syntax for array iteration

As discussed earlier, the for loop also has a syntax similar to that of the C programming language. This alternative syntax provides another way to iterate over an array. Let's use this C-style syntax to iterate over the countries array.

Open your demo.sh file and replace the previous script with the following:

#!/bin/bash

# Define your array
countries=("India" "France" "United Kingdom")

# Get array length
length=${#countries[@]}

# Loop through the array
for ((i=0; i<$length; i++))
do
   echo "${countries[$i]}"
done

In this script, we first define our array as we did previously. Then we calculate the length (the number of elements) of the array using ${#countries[@]}. The # symbol is used here to denote length. Then we store the array length in the length variable.

Following that, we use a C-style for loop to iterate over the array. The syntax ((i=0; i<$length; i++)) means we start with i equal to 0, continue as long as i is less than the length of the array, and increment i by 1 after each loop iteration.

Inside the loop, we use echo "${countries[$i]}" to print both the index and its corresponding element of the array. Here, "${countries[$i]}" represents the array value at the index $i.

After you've replaced the script, save your changes and close the editor. Now you can run the updated script with the same bash demo.sh command. You should see the same output as before, with the values of the countries array printed to your terminal one after another, like this:

Understanding array expansion operators and double quotes

By now, you're familiar with using for loops in Bash scripts to iterate over indexed arrays. To take your understanding of the for loops to the next level, it's important to understand two additional concepts that are crucial to their function: array expansion operators and the use of double quotes.

Open your demo.sh file and replace the previous script with the following:

#!/bin/bash

# Define your array
countries=("India" "France" "United Kingdom")

# Loop through the array
for country in "${countries[@]}"
do
   echo "$country"
done

In this script, the @ symbol inside "${countries[@]}" represents an array expansion operator. An array expansion operator is a special construct that allows us to access all the elements of an array. When we use "${countries[@]}" in our script, we are telling the script to expand the array, i.e., to treat each element of the array as a separate entity.

Save your changes and run the updated script with the bash demo.sh command. You should see the values of the countries array printed to your terminal on separate lines, like this:

There’s another operator - the * symbol - that's often used to access all elements in an array. You might think @ and *  are interchangeable, but they are not. There’s a subtle difference between them. Let's understand this difference in action.

The difference between @ and *

First, update the array expansion in the for loop inside the script from "${countries[@]}" to "${countries[*]}". We have only replaced the @ symbol with the * symbol. Now, save the changes, close the editor, and run the script. You’ll see the following output:

This output is a single string. All of the elements of the array are joined together into one continuous string of characters, separated by spaces.

The key point to note here is that when we use the * expansion operator inside "${countries[*]}", Bash does not distinguish between the individual elements in the array anymore. Even though the array countries has three elements, Bash treats the expanded array as one single string when we use * inside double quotes.

On the other hand, when we use the @ expansion operator inside "${countries[@]}", each element of the array is treated separately, preserving the integrity of multi-word elements like United Kingdom.

The role of double quotes in array expansion

Notice that there are double quotes (" ") surrounding the array expansion "${countries[@]}". In the context of array expansion in Bash, these double quotes play an important role. Let’s understand what this role is.

In the for loop inside the script, remove the double quotes around the array expansion (the array expansion operator could be @ or *). Then run the script. You'll notice that United Kingdom is split into two separate words: United and Kingdom, as shown below:

Without double quotes, Bash performs word splitting on the array elements, treating spaces within elements as word separators.

Therefore, to correctly handle all elements of an array (especially when they may contain spaces or special characters), it's recommended to use "${array[@]}" in your scripts. This approach will ensure your array elements remain intact and are treated as they were intended.

Understanding these nuances of array expansion operators and double quotes will help you to write more robust and effective Bash scripts."

Bash Script For Looping Through Associative Arrays With a For Loop

Just like indexed arrays, you can use a for loop to iterate over associative arrays in Bash. However, associative arrays use keys instead of numerical indices, so the loop will look a bit different. Let's walk through an example.

Open your demo.sh file and replace the previous script with the following:

#!/bin/bash

# Define your associative array
declare -A country_capital

country_capital=(["India"]="New Delhi" ["France"]="Paris" ["United Kingdom"]="London")

# Loop over the array
for key in "${!country_capital[@]}"
do
   echo "Key: $key, Value: ${country_capital[$key]}"
done

In this loop, we use "${!array[@]}" to generate all the keys of the associative array. For each key, we then print the key itself as well as its associated value.

Now run the script. You should see the keys and their corresponding values printed to your console, appearing one after another, similar to this:

Notice that even though India was the first key we added to our associative array, it doesn't appear as the first entry in the output. In fact, the output could appear in any order.

So why is India not the first item in the output, and why might the output change each time you run the script? This is because, in Bash, the order of the elements in an associative array is not guaranteed to match the order in which they were added.

Conclusion

Congratulations! Now you know how to write Bash scripts that use for loops to iterate over both indexed and associative arrays. You've learned how to use array expansion operators and understood the differences between @ and *. You've also seen the importance of double quotes in preserving the integrity of array elements.

Looking to take your Bash scripting skills to the next level? Check out this course from KodeKloud:

  • Advanced Bash Scripting: In this course, you'll start with fundamentals like variables, functions, and parameter expansions, and then dive deeper into streams, input/output redirection, and command-line utilities like awk and sed. You'll master arrays for data manipulation and storage, and learn best practices to create robust scripts.