How to Use Loops in Bash?

August 5, 2023

Bash scripting offers a variety of loops that allow you to repeatedly execute a sequence of commands. Whether you're a beginner in bash scripting or an experienced developer, understanding how to use loops effectively is a fundamental skill. In this comprehensive guide, we will explore different types of loops in bash, their applications, and provide examples to help you master the art of loop scripting.

This is part of my series on bash. Look at all the articles over here.

Introduction to Loops in Bash

Loops form an essential part of bash scripting, allowing you to perform repetitive tasks efficiently. They enable you to iterate through lists, arrays, files, and even command outputs, executing a set of instructions for each iteration. Bash provides several types of loops, each tailored to specific scenarios.

The Basic For Loop

The basic for loop is your starting point in loop scripting. It iterates over a range of values and performs actions for each value. Consider the following example:

for i in {1..5}; do
    echo "Number: $i"
done

Output:

Number: 1
Number: 2
Number: 3
Number: 4
Number: 5

In this loop, the variable i takes values from 1 to 5, and the echo command displays each number along with the descriptive text "Number: ". This type of loop is particularly useful when you need to execute a specific action a predetermined number of times.

To learn about braces expansion in bash (). Look at this article.

Looping Through Arrays

Arrays are fundamental data structures in bash, and you can use loops to iterate through their elements. Take a look at this example:

fruits=("apple" "banana" "orange")
for fruit in "${fruits[@]}"; do
    echo "Fruit: $fruit"
done

Output:

Fruit: apple
Fruit: banana
Fruit: orange

This loop cycles through the elements of the fruits array, displaying each fruit's name along with "Fruit: ". This approach is beneficial when you want to process each element in an array individually.

Note: Here echo command is being used to print on terminal. Learn more about echo in detail in this article.

The Versatile While Loop

The while loop continuously executes a block of code as long as a specific condition remains true. Here's an example:

count=1
while [ $count -le 3 ]; do
    echo "Count: $count"
    count=$((count+1))
done

Output:

Count: 1
Count: 2
Count: 3

In this instance, the loop prints the value of count as long as it is less than or equal to 3. The while loop is ideal for scenarios where the number of iterations is not predetermined.

The Purpose of the Until Loops in bash

The until loop is the counterpart of the while loop; it continues executing until a given condition becomes true. Consider this illustration:

num=0
until [ $num -ge 5 ]; do
    echo "Number: $num"
    num=$((num+1))
done

Output:

Number: 0
Number: 1
Number: 2
Number: 3
Number: 4

Here, the loop prints the value of num until it becomes greater than or equal to 5. The until loop is advantageous when you want to repeat a task until a specific criterion is met.

Iterating Through Files with Loops

Looping through files is a common scenario in bash scripting. The following example showcases this:

for i in '/home/ajay/Videos/OP/1063.mkv' '/home/ajay/Videos/OP/1064.mkv' '/home/ajay/Videos/OP/1065.mkv'
do
  ffmpeg -i "$i" -codec:v libx265 "${i}_x265.mkv"
done

This loop compresses each video file using ffmpeg*. It's a handy technique when you need to perform batch operations on files.

To learn more about ffmpeg compression of videos and images, look at this article.

Adding Incremental Steps to Loops in bash

Loops can include incremental steps to control the iteration process. Observe this example:

for ((i=0; i<10; i+=2)); do
    echo "Step: $i"
done

Output:

Step: 0
Step: 2
Step: 4
Step: 6
Step: 8

Here, the loop iterates through numbers starting from 0, increasing by 2 in each iteration. This feature is valuable for scenarios where you need to process every nth element.

Looping Through Command Output

Looping through the output of commands is another valuable capability of bash loops. Consider the following script:

for file in $(ls *.txt)
do
    echo "Text File: $file"
done

Output:

Text File: ascii.txt
Text File: file_1.txt
Text File: file1.txt
Text File: file_2.txt
Text File: file_3.txt
Text File: file_4.txt
Text File: file_5.txt
Text File: file.txt
Text File: romeo.txt
Text File: sample.txt
Text File: test.txt
Text File: unicode.txt
Text File: utf8.txt

In this example, the loop goes through the output of the command ls *.txt and prints them. This technique is useful when you want to process data generated by a command.

Implementing Conditional Loops in bash

Conditional loops combine loops and conditional statements to execute commands based on specific criteria. Take a look at this illustration:

for num in {1..5}; do
    if [ $num -gt 2 ]; then
        echo "Number: $num"
    fi
done

Output:

Number: 3
Number: 4
Number: 5

This loop prints numbers greater than 2. It's an effective approach when you need to filter elements during iteration.

Skipping Iterations with the continue Statement

The continue statement allows you to skip iterations within a loop based on certain conditions. Explore this example:

for num in {1..5}; do
    if [ $num -eq 3 ]; then
        continue
    fi
    echo "Number: $num"
done

Output:

Number: 1
Number: 2
Number: 4
Number: 5

In this loop, the iteration with the value 3 is skipped due to the continue statement. This feature is handy for excluding specific cases from processing.

Breaking Out of Loops with the break Statement in bash

The break statement provides a way to prematurely exit a loop based on certain conditions. Consider this example:

for num in {1..5}; do
    if [ $num -eq 4 ]; then
        break
    fi
    echo "Number: $num"
done

Output:

Number: 1
Number: 2
Number: 3

The loop terminates when the value of num becomes 4. The break statement is useful for stopping execution once a particular condition is met.

Looping through File content

Bash loops can also operate on data from files. The following script showcases this technique.

[ajay@legion Pictures]$ cat numbers.txt
5
8
3
9
2
while read -r number; do
    echo "Number: $number"
done < numbers.txt

Output:

Number: 5
Number: 8
Number: 3
Number: 9
Number: 2

Here, the loop reads numbers from the numbers.txt file and prints them using echo. This approach is useful for processing data from external sources.

Expanding Horizons: Looping Through Multiple Ranges

Bash loops can handle more complex scenarios, such as iterating through multiple ranges. This example demonstrates the concept:

for i in ~/{f..b}/Downloads/{1..-2}; do
    echo $i
done

Output:

/home/ajay/f/Downloads/1
/home/ajay/f/Downloads/0
/home/ajay/f/Downloads/-1
/home/ajay/f/Downloads/-2
/home/ajay/e/Downloads/1
/home/ajay/e/Downloads/0
/home/ajay/e/Downloads/-1
/home/ajay/e/Downloads/-2
/home/ajay/d/Downloads/1
/home/ajay/d/Downloads/0
/home/ajay/d/Downloads/-1
/home/ajay/d/Downloads/-2
/home/ajay/c/Downloads/1
/home/ajay/c/Downloads/0
/home/ajay/c/Downloads/-1
/home/ajay/c/Downloads/-2
/home/ajay/b/Downloads/1
/home/ajay/b/Downloads/0
/home/ajay/b/Downloads/-1
/home/ajay/b/Downloads/-2

In this intricate loop, multiple ranges are combined to generate a variety of paths. This technique is particularly useful when you need to work with dynamic data patterns.

Creative Usage: Creating a Timer with Loops

Loops can be creatively employed for various tasks. Here's an example script* of using loops to create a simple timer:

#!/bin/bash

# convert time into seconds
time="$(echo "$@" | sed 's/h/*3600+/g;s/m/*60+/g;s/s/*1+/g;s/+$//' | bc | sed 's/\..*//')"

# print the remaining time
for ((i=1;i<=time;i=i+1))
do
  printf '\033[0K%s\r' "$(((time-i)/60)):$(((time-i)%60))"
  sleep 1
done

Output:

23:56

For this to work, first you need to create a script with a name, say timer.sh. To learn about how to work with script, look over here.

This script accepts input in hours, minutes, and seconds, converts it to seconds, and then creates a countdown timer that prints the remaining time in the format "minutes:seconds" (ex - 23:56 -> 23:55 -> 23:54...).

You can run commands like timer.sh 1h 3s or timer.sh 30m 5m 2s.

You can also append a music after the loop using ffplay and/or add a notification using notify-send, to get notification and/or music after the time is over.

It's a unique example of how loops can be applied to creative real-world scenarios.

Conclusion

Loops are the backbone of bash scripting, empowering you to automate repetitive tasks and manage complex operations. Whether you're iterating through arrays, files, or command outputs, understanding the different types of loops and their applications is essential. By mastering loop scripting in bash, you unlock the potential to create efficient and robust automation solutions for various scenarios. For official guide from GNU, look at this manual.