Repeating Instructions With Loops in Bash

16 min read·Jan 1, 2025

In programming, a loop statement is a control structure used to repeat the execution of a set of instructions multiple times until a certain condition is met.

The while loop

The while loop is used to execute a set of instructions specified between the do and done keywords, for as long as the condition remains true.

while [[ condition ]];
do
  instructions
done

This structure is often used to create long-running processes or to process data of unknown length.

Note: In programming, the cycle in which a loop executes its instructions — whether partially or completely — is called an iteration. For example, if a loop executes the specified instructions 3 times, we'll say that it performed 3 iterations.

Example

Let's consider this script, that outputs the elements of an array of strings:

#!/bin/bash

guests=("John Doe" "James Higgins" "Alice Peterson")
total=${#guests[@]}
index=0

while [[ $index -ne $total ]];
do
  echo "$index -> ${guests[$index]}"
  index=$(($index + 1))
done

When executed, it will:

  1. Declare a variable named guests and initialize it with an array of strings.
  2. Declare a variable named total and initialize it to the number of elements in the guests array.
  3. Declare a variable named index and initialize to 0.
  4. Start a loop that runs while the value of the index variable is not equal to the value of the total variable.
  5. Output the value of the index variable and the value of the element in the guests array at this index.
  6. Increment the value of the index variable by 1.
  7. Re-run the loop from step 5.

Which will produce this output:

$ ./script.sh
0 -> John Doe
1 -> James Higgins
2 -> Alice Peterson

The until loop

The until loop is used to execute a set of instructions specified between the do and done keywords, until the condition becomes true.

until [[ condition ]];
do
  instructions
done

Example

Let's consider this script, that outputs the value of a variable and decrements it at every iteration:

#!/bin/bash

counter=3

until [[ $counter -lt 1 ]]; do
  echo "Countdown: $counter"
  counter=$((counter - 1))
done

echo "Blast off!"

When executed, it will:

  1. Declare a variable named counter and initialize it to 3.
  2. Evaluate whether the value of the counter variable is lesser than 1.
  3. If true, terminate the until loop.
  4. If false, output the current value of the counter variable and decrease it by 1.
  5. Re-run the until loop from step 2.

Which will produce this output:

$ ./script.sh
Countdown: 3
Countdown: 2
Countdown: 1
Blast off!

The for loop

The for loop is used to execute a set of instructions a predetermined number of times based on a known list of items or range.

Iterating on a list

In its first form, the for loop iterates over a list of items, such as an array of strings or numbers, and executes the instructions specified between the do and done keywords for each item until there are no more items in the list.

for item in list;
do
  instructions
done

Where:

  • item is an arbitrary variable name that can be used in the instructions declared between the do and done keywords
  • list is a list of values separated by a space character, generated either manually or automatically.

Note: If the in list expression is not specified, the for loop assumes that the list is composed of the positional arguments of the script, as if in "$@" had been specified.

Example

Let's consider this script, that extracts and outputs the email address of a list of users contained in an array:

#!/bin/bash

users=("John Doe,johndoe@mail.com" "Alice Fisher,a.fisher@mail.com" "Bob Burnquist,bbquist@mail.com")

for user in "${users[@]}";
do
  echo "$user" | cut -d ',' -f 2
done

When executed, it will:

  1. Define a variable named users and initialize it with an array of strings.
  2. Define a variable named user and initialize it with the value of the next element in the users array.
  3. Split the current value of the user variable using ',' as a delimiting character and output the second part of the string.
  4. Re-run the for loop from step 2.

Which will produce this output:

$ ./script.sh
johndoe@mail.com
a.fisher@mail.com
bbquist@mail.com

Note:

The values of a list can also be automatically generated using a shell expansion, such as a brace expansion, a command substitution, and so on.

For example:

# Shell expansion
for i in {1..3}

# Command substitution
for i in $(seq 1 3)

Iterating on a condition

In its second form inspired from the C language, the for loop executes a set of instructions until its conditional expression evaluates to false.

for (( init ; cond ; iter ));
do
  instructions
done

Where:

  • init is an initialization expression that is executed once at the beginning of the loop and is typically used to set the initial values for loop control variables.
  • cond is a conditional expression that is evaluated before each iteration of the loop and will keep the loop running until it evaluates to false.
  • iter is an iteration expression that is executed at the end of each iteration and is typically used to update loop control variables.

Example

Let's consider this script, that outputs all the possible combinations between two arrays of strings:

#!/bin/bash

first_names=("John" "Alice" "Bob")
last_names=("Doe" "Smith" "Johnson")

for ((i = 0; i < ${#first_names[@]}; i++));
do
  for ((j = 0; j < ${#last_names[@]}; j++));
  do
    echo "${first_names[i]} ${last_names[j]}"
  done
done

When executed, it will:

  1. Define a variable named first_names and initialize it with an array of strings.
  2. Define a variable named last_names and initialize it with an array of strings.
  3. Start a first for loop that iterates on each index of the first_names array.
  4. Start a nested for loop that iterates on each index of the last_names array.
  5. Output the current value of the first_names array concatenated to the current value of the last_names array.

Which will produce this output:

$ ./script.sh
John Doe
John Smith
John Johnson
Alice Doe
Alice Smith
Alice Johnson
Bob Doe
Bob Smith
Bob Johnson

Note: In Bash, the variable++ syntax is a shorthand for increasing the value of a variable by 1 (i.e. incrementing), which is equivalent to variable=$(($variable + 1)).

Terminating and skipping loop iterations

In Bash, the break and continue statements allow you to control the execution flow of loops by either terminating them prematurely or skipping some iterations.

The break statement

The break statement is used to immediately terminate the current iteration of a loop, regardless of whether the loop's condition is met or not.

# for / while / until
do
  if [[ condition ]]; then
    break
  fi
  instructions
done

Example

Let's consider this script, that checks the validity of email addresses:

#!/bin/bash

emails=("$@")

for email in "${emails[@]}"; do
  if [[ ! "$email" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$ ]];
  then
    echo "Invalid email address: $email"
    break
  fi
done

When executed, it will:

  1. Declare a variable named emails and initialize it with an array containing the list of positional arguments of the script.
  2. Loop on each element of the emails array and store the value of the current element into the email variable.
  3. Check if the value of the email variable matches the specified regular expression.
  4. If false, output the value of the email variable and terminate the loop using the break statement.

Which will produce this output:

$ ./script.sh johndoe@mail.com a.ghallan@mail.com bobfisher03 martin.gould@mail.com
Invalid email address: bobfisher03

The continue statement

The continue statement is used to skip the current iteration of a loop and directly proceed to the next iteration, without executing the remaining instructions.

# for / while / until
do
  if [[ condition ]]; then
    continue
  fi
  instructions
done

Example

Let's consider this script, that outputs the filenames of the entries of the current working directory with a .js file extension:

#!/bin/bash

for file in *;
do
  if [[ ! $file == *.js ]]; then
    continue
  fi
  echo $file
done

When executed, it will:

  1. Loop on each file present in the current directory and store the name of the current file in the file variable.
  2. Check if the value of the file variable ends with ".js".
  3. If true, skip the current loop iteration without executing the following instructions using the continue keyword.
  4. If false, output the current filename using the echo command.
  5. Re-run the loop from step 1.

Which will produce this output:

$ ls
index.js   package.json   script.sh
$ ./script.sh
index.js

Summary

Here's a summary of what you've learned in this lesson:

  • The while loop executes a set of instructions for as long as the specified condition remains true.
  • The until loop executes a set of instructions until the specified condition becomes true.
  • The for loop iteratively executes a set of instructions a predetermined number of times based on a known list of items or range.
  • The break statement is used to immediately terminate the current loop, regardless of whether the loop's condition is met or not.
  • The continue statement is used to skip the current iteration of a loop and directly proceed to the next iteration, without executing the remaining instructions.

Enjoying the courses?

I've made these courses completely free so anyone can learn from them. If they've helped you and you'd like to actively support the work behind BackendBrewery, you can leave a tip:

Support BackendBrewery
Repeating Instructions With Loops in Bash | Backend Brewery