The for loop is one of the most fundamental constructs in Bash scripting. Whether you are renaming hundreds of files, automating server backups, iterating over a list of IP addresses, or processing command outputs, mastering the for loop saves you hours of repetitive command-line work.
In this tutorial, we will explore 10 practical, real-world examples of Bash for loops in Linux, starting from basic syntax and moving to advanced automation patterns.
Table of Contents
Basic Bash For Loop Syntax
In Bash, a standard for loop iterates through a list of items and executes a block of commands for each element:
for item in item1 item2 item3 ...
do
# Commands to execute for each item
echo "$item"
done
You can also write it as a concise one-liner by separating statements with semicolons:
for item in item1 item2 item3; do echo "$item"; done
Let’s dive into 10 practical examples.
1. Loop Through a List of Static Strings
The simplest way to use a for loop is iterating over hardcoded items separated by spaces.
#!/usr/bin/env bash
for fruit in Apple Banana Cherry Orange
do
echo "Fruit: $fruit"
done
Output:
Fruit: Apple
Fruit: Banana
Fruit: Cherry
Fruit: Orange
2. Loop Through a Range of Numbers (Brace Expansion)
Using Bash brace expansion {start..end}, you can generate a sequence of numbers without writing external utilities like seq.
#!/usr/bin/env bash
for i in {1..5}
do
echo "Iteration number: $i"
done
Output:
Iteration number: 1
Iteration number: 2
Iteration number: 3
Iteration number: 4
Iteration number: 5
3. Loop with Step Increments
You can specify a step interval using {start..end..step}:
See also: Mastering the Linux Command Line — Your Complete Free Training Guide
#!/usr/bin/env bash
# Count from 0 to 20 by 5s
for num in {0..20..5}
do
echo "Value: $num"
done
Output:
Value: 0
Value: 5
Value: 10
Value: 15
Value: 20
4. C-Style (Three-Expression) For Loop
Bash supports the classic C-style for (( initialization; condition; increment )) syntax, ideal for arithmetic-heavy counters.
#!/usr/bin/env bash
for (( i=1; i<=5; i++ ))
do
echo "Counter: $i"
done
Output:
Counter: 1
Counter: 2
Counter: 3
Counter: 4
Counter: 5
5. Iterate Over Files in a Directory (Globbing)
You can iterate through files using wildcards (glob patterns). This pattern is widely used in batch file processing and backups.
#!/usr/bin/env bash
for file in /var/log/*.log
do
if [ -f "$file" ]; then
echo "Found log file: $file"
fi
done
Sample Output:
Found log file: /var/log/alternatives.log
Found log file: /var/log/dpkg.log
Found log file: /var/log/auth.log
Tip: Always quote variable references (e.g.,
"$file") to handle filenames that contain whitespace.
6. Batch Rename Files with a For Loop
Combining a for loop with parameter expansion makes bulk-renaming operations quick and straightforward:
#!/usr/bin/env bash
# Rename all .txt files to .bak
for f in *.txt
do
[ -e "$f" ] || continue
mv "$f" "${f%.txt}.bak"
echo "Renamed: $f -> ${f%.txt}.bak"
done
Sample Output:
Renamed: report1.txt -> report1.bak
Renamed: report2.txt -> report2.bak
Renamed: summary.txt -> summary.bak
7. Iterate Over Command Output (Command Substitution)
Using $(command), you can loop over the lines or words generated by an external Linux command.
#!/usr/bin/env bash
for user in $(cut -d: -f1 /etc/passwd | head -n 4)
do
echo "System user: $user"
done
Sample Output:
System user: root
System user: daemon
System user: bin
System user: sys
8. Iterate Over Array Elements
Bash arrays paired with for loops allow robust handling of multi-element data structures:
#!/usr/bin/env bash
servers=("web01.example.com" "web02.example.com" "db01.example.com")
for server in "${servers[@]}"
do
echo "Checking host status: $server"
done
Output:
Checking host status: web01.example.com
Checking host status: web02.example.com
Checking host status: db01.example.com
9. Ping Multiple IP Addresses / Hosts
A standard DevOps task: health-checking a subnet or list of targets.
#!/usr/bin/env bash
for ip in 192.168.1.{1..3}
do
if ping -c 1 -W 1 "$ip" > /dev/null 2>&1; then
echo "[UP] $ip is reachable"
else
echo "[DOWN] $ip is unreachable"
fi
done
Sample Output:
[UP] 192.168.1.1 is reachable
[UP] 192.168.1.2 is reachable
[DOWN] 192.168.1.3 is unreachable
10. Loop Control: Using break and continue
Use break to exit the loop early when a condition is met, and continue to skip the remainder of the current iteration.
#!/usr/bin/env bash
for num in {1..7}
do
# Skip number 3
if [ "$num" -eq 3 ]; then
echo "Skipping $num..."
continue
fi
# Terminate loop when reaching 6
if [ "$num" -eq 6 ]; then
echo "Found $num, terminating loop."
break
fi
echo "Processing number: $num"
done
Output:
Processing number: 1
Processing number: 2
Skipping 3...
Processing number: 4
Processing number: 5
Found 6, terminating loop.
Summary Cheat Sheet
| Loop Pattern | Example Syntax | Best Use Case |
|---|---|---|
| Static List | for x in a b c; do ...; done | Fixed set of values/arguments |
| Number Range | for i in {1..10}; do ...; done | Sequential loops & counters |
| Step Interval | for i in {0..100..10}; do ...; done | Incremental stepping & sampling |
| C-Style Loop | for ((i=0; i<n; i++)); do ...; done | Arithmetic indexing & conditions |
| Glob / Files | for f in *.log; do ...; done | File system batch operations |
| Command Output | for x in $(cat list.txt); do ...; done | Processing dynamic tool output |
| Array | for el in "${arr[@]}"; do ...; done | Structured arrays & safety with spaces |
Best Practices for Bash Loops
- Always quote variables (
"$file","$item") to prevent word splitting issues with filenames containing spaces. - Handle empty glob matches with
[ -e "$f" ] || continueto avoid processing the literal wildcard string (e.g.,*.txt) when no files match. - Prefer read loops (
while IFS= read -r line) overfor item in $(cat file.txt)when processing files line by line, asforsplits by any whitespace.


