Fading Coder

One Final Commit for the Last Sprint

Home > Notes > Content

Shell Loop Constructs: for, while, until, and select

Notes May 10 4

Shell scripts provide several loop mechanisms for repetitive task automation. This guide covers the main loop types available in Bash.

Loop Types Overview

Loop Type Purpose
for Iterate over a list or sequence
while Execute while a condition is true
until Execute until a condition becomes true
select Create numbered menus

For Loop

The for loop in Shell operates in three variations.

List-Based Iteration

for variable in item1 item2 item3
do
    commands
done

The loop assigns each list item to the variable sequential, executing the loop body for each value until the list is exhausted. List items are separated by whitespace.

Example - Iterating through host addresses:

#!/bin/bash

for host in 192.168.72.130 192.168.72.131 192.168.72.132
do
    echo "Pinging $host"
    ping -c 1 -W 1 "$host" > /dev/null 2>&1 && echo "$host is reachable"
done

Using brace expansion:

#!/bin/bash

for num in {0..3}
do
    echo "Server-$num"
done

Generating sequences with seq:

#!/bin/bash

for item in $(seq 1 2 10)
do
    echo "Processing item $item"
done

Specifying step increments:

#!/bin/bash

for n in {1..10..2}
do
    echo "Odd number: $n"
done

Filtering directory contents:

#!/bin/bash

for entry in $(ls /etc)
do
    if [ -f "/etc/$entry" ]; then
        echo "File: $entry"
    fi
done

Processing words with length check:

#!/bin/bash

words="hello world rabbit favorite eat apple cabbage"

for w in $words
do
    length=${#w}
    if [ $length -le 6 ]; then
        echo "$w (length: $length)"
    fi
done

Loop Without Explicit List

for variable
do
    commands
done

This form iterates over positional parameters.

#!/bin/bash

for arg in "$@"
do
    echo "Argument: $arg"
done

C-Style For Loop

for ((expr1; expr2; expr3))
do
    commands
done

Example - Creating user accounts:

#!/bin/bash

for ((counter=1; counter<=25; counter++))
do
    if [ $counter -lt 10 ]; then
        username="test0$counter"
    else
        username="test$counter"
    fi
    
    if ! id "$username" &>/dev/null; then
        useradd "$username"
        echo "DefaultPass1!" | passwd --stdin "$username" &>/dev/null
        echo "Created user: $username"
    fi
done

Alternative approach using seq formatting:

#!/bin/bash

for u in $(seq -f "user%02g" 1 20)
do
    if ! id "$u" &>/dev/null; then
        useradd "$u"
        echo "Created: $u"
    fi
done

While Loop

Basic Syntax

while [ condition ]
do
    commands
done

Example - Counting with while:

#!/bin/bash

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

Reading files line by line - Method 1:

#!/bin/bash

filename="data.txt"

while IFS= read -r line
do
    echo "$line"
done < "$filename"

Reading files line by line - Method 2:

#!/bin/bash

exec < "data.txt"

while read -r content
do
    echo "$content"
done

Infinite Loops

Three approaches for continuous execution:

while true; do
    commands
done
while :; do
    commands
done
while [ 1 -eq 1 ]; do
    commands
done

Practical Examples

Number guessing game:

#!/bin/bash

target=$((RANDOM % 50 + 1))
attempts=0
max_attempts=5

while true
do
    read -p "Enter a number (1-50): " guess
    ((attempts++))
    
    if [ "$guess" -eq "$target" ]; then
        echo "Correct! You got it in $attempts tries."
        exit 0
    elif [ "$guess" -gt "$target" ]; then
        echo "Too high, try again."
    else
        echo "Too low, try again."
    fi
    
    if [ $attempts -ge $max_attempts ]; then
        echo "Game over! The number was $target."
        exit 1
    fi
done

Parsing structured data:

Input file servers.txt:

192.168.1.10  8080
192.168.1.11  9090
192.168.1.12  80
#!/bin/bash

while read -r ip port
do
    echo "Server IP: $ip, Service Port: $port"
done < servers.txt

Until Loop

The until loop contiunes execution while the condition is false, stopping when it becomes true.

until [ condition ]
do
    commands
done

Example - Counting to five:

#!/bin/bash

i=0

until [ $i -ge 5 ]
do
    echo "Value: $i"
    i=$((i + 1))
done

Comparing while and until:

#!/bin/bash

number=0

# Using while - loop runs while condition is true
while [ $number -lt 3 ]
do
    echo "While: $number"
    ((number++))
done

# Using until - loop runs until condition becomes true
number=0
until [ $number -ge 3 ]
do
    echo "Until: $number"
    ((number++))
done

Select Loop

The select construct generates a numbered menu from a list.

select variable [ in list ]
do
    commands
done

Example - Database version selection:

#!/bin/bash

echo "Select database version:"
select db_ver in "MariaDB-10.3" "MariaDB-10.5" "MariaDB-10.6" "PostgreSQL-14"
do
    echo "Installing $db_ver..."
    break
done

Example - Application menu:

#!/bin/bash

echo "Choose an application:"
select app in "Web Server" "Database" "Cache" "Message Queue" "Exit"
do
    case $app in
        "Exit") echo "Goodbye"; break ;;
        *) echo "Selected: $app" ;;
    esac
done

Example - Color selection with auto-exit:

#!/bin/bash

select color in "Red" "Green" "Blue" "Yellow" "Quit"
do
    case $REPLY in
        5) echo "Exiting..."; break ;;
        *) echo "You selected: $color" ;;
    esac
done

Nested Loops

Loops can be nested within other loops for complex iterations.

Example - Multiplication table:

#!/bin/bash

for row in {1..9}
do
    line=""
    for col in {1..9}
    do
        if [ $col -le $row ]; then
            product=$((row * col))
            line+="${row}x${col}=${product}  "
        fi
    done
    echo "$line"
done

Example - Diamond pattern:

#!/bin/bash

rows=7

for ((r=1; r<=rows; r++))
do
    for ((s=rows-r; s>0; s--))
    do
        echo -n " "
    done
    for ((c=1; c<=r; c++))
    do
        echo -n "* "
    done
    echo
done

for ((r=rows-1; r>=1; r--))
do
    for ((s=rows-r; s>0; s--))
    do
        echo -n " "
    done
    for ((c=1; c<=r; c++))
    do
        echo -n "* "
    done
    echo
done

Example - File processing within directories:

#!/bin/bash

for dir in /tmp/project1 /tmp/project2 /tmp/project3
do
    if [ -d "$dir" ]; then
        echo "Processing directory: $dir"
        count=0
        for file in "$dir"/*
        do
            if [ -f "$file" ]; then
                ((count++))
            fi
        done
        echo "  Found $count files"
    fi
done

Loop Control: break and continue

break Statement

The break statement immediately exits the innermost loop.

#!/bin/bash

for n in {1..10}
do
    if [ $n -eq 7 ]; then
        echo "Found target at $n, stopping"
        break
    fi
    echo "Checking $n"
done
echo "Loop terminated"

Breaking outer loops with label:

#!/bin/bash

outer_loop:
for i in {1..5}
do
    for j in {1..5}
    do
        if [ $((i * j)) -eq 12 ]; then
            echo "Found match: $i x $j = 12"
            break 2
        fi
    done
done

continue Statement

The cotninue statement skips the rest of the current iteration and moves to the next cycle.

#!/bin/bash

for n in {1..10}
do
    if [ $((n % 2)) -eq 0 ]; then
        continue
    fi
    echo "Odd number: $n"
done

Skipping specific values:

#!/bin/bash

for status in active inactive suspended pending deleted
do
    if [ "$status" = "deleted" ]; then
        continue
    fi
    echo "Processing $status accounts"
done

Using continue in nested loops:

#!/bin/bash

for outer in {1..3}
do
    for inner in {1..3}
    do
        if [ $inner -eq 2 ]; then
            continue
        fi
        echo "$outer - $inner"
    done
done

Related Articles

Designing Alertmanager Templates for Prometheus Notifications

How to craft Alertmanager templates to format alert messages, improving clarity and presentation. Alertmanager uses Go’s text/template engine with additional helper functions. Alerting rules referenc...

Deploying a Maven Web Application to Tomcat 9 Using the Tomcat Manager

Tomcat 9 does not provide a dedicated Maven plugin. The Tomcat Manager interface, however, is backward-compatible, so the Tomcat 7 Maven Plugin can be used to deploy to Tomcat 9. This guide shows two...

Skipping Errors in MySQL Asynchronous Replication

When a replica halts because the SQL thread encounters an error, you can resume replication by skipping the problematic event(s). Two common approaches are available. Methods to Skip Errors 1) Skip a...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.