Bash for loops explained banner
Bash focus guide

Bash For Loops Explained

Bash for loops let you repeat commands for each item in a list. They are useful for looping through files, services, domains, users, backups, logs and almost any repeated Linux admin task.

On this page
Basics

Basic Bash for loop syntax

The basic pattern is:

for item in list; do
  command "$item"
done

Example:

for name in alpha beta gamma; do
  echo "$name"
done
$ bash names.sh
alpha
beta
gamma

The variable name changes on each loop. First it is alpha, then beta, then gamma.

Files

Loop through files

A common use for for loops is running a command against matching files.

for file in *.log; do
  echo "Found log file: $file"
done
$ ./list-logs.sh
Found log file: access.log
Found log file: error.log
Found log file: debug.log

The shell expands *.log before the loop runs. That means the loop sees a list of matching filenames.

Safer file loops

Safely handle missing matches

If no .log files exist, Bash may leave the pattern unchanged. This can produce confusing output.

for file in ./*.log; do
  [ -e "$file" ] || continue
  echo "$file"
done

The line [ -e "$file" ] || continue means: if the path does not exist, skip this loop item.

Use "$file", not $file. Quoting variables helps protect filenames with spaces.
Important warning

Be careful looping through command output

This pattern is common, but risky:

for file in $(find . -name "*.log"); do
  echo "$file"
done

It breaks when filenames contain spaces, tabs or unusual characters. A safer pattern is:

find . -name "*.log" -print0 | while IFS= read -r -d '' file; do
  echo "$file"
done
$ ./safe-find-loop.sh
./access.log
./old logs/error log 2026.log
./debug.log

For simple beginner scripts, looping over ./*.log is fine. For recursive file searches, prefer the safer find -print0 pattern.

Arrays

Loop through a Bash array

Arrays are useful when you have a fixed list, such as services or domains.

SERVICES=("nginx" "mysql" "sshd")

for service in "${SERVICES[@]}"; do
  echo "$service"
done
$ ./services-list.sh
nginx
mysql
sshd

The quoted "${SERVICES[@]}" form is the safe way to loop through each array item.

Practical script

Check multiple Linux services

#!/usr/bin/env bash

SERVICES=("nginx" "mysql" "sshd")

for service in "${SERVICES[@]}"; do
  if systemctl is-active --quiet "$service"; then
    echo "[OK] $service is running"
  else
    echo "[WARN] $service is not running"
  fi
done
$ ./check-services.sh
[OK] nginx is running
[WARN] mysql is not running
[OK] sshd is running

This is a practical loop for Linux troubleshooting. For more service commands, see the systemd guide.

Practical script

Check multiple domains with dig

#!/usr/bin/env bash

DOMAINS=("example.com" "example.org" "commandlinequiz.com")

for domain in "${DOMAINS[@]}"; do
  echo "== $domain =="
  dig +short "$domain"
  echo
done
$ ./check-domains.sh
== example.com ==
93.184.216.34

== example.org ==
93.184.216.34

== commandlinequiz.com ==
104.21.10.123
172.67.150.45

For more DNS troubleshooting, read the dig command guide.

Counting loops

C-style Bash for loops

Bash also supports a counting style loop.

for ((i = 1; i <= 5; i++)); do
  echo "Attempt $i"
done
$ ./attempts.sh
Attempt 1
Attempt 2
Attempt 3
Attempt 4
Attempt 5

This is useful for counters, retries and simple repeated attempts.

Control flow

Using break and continue

continue skips to the next loop item. break exits the loop completely.

for file in ./*.log; do
  [ -e "$file" ] || continue

  if [ "$file" = "./error.log" ]; then
    echo "Found error.log"
    break
  fi
done
$ ./find-error-log.sh
Found error.log
Mistakes

Common Bash for loop mistakes

MistakeProblemBetter option
for file in $(ls)Breaks with spaces and odd filenames.Use globs or find -print0.
echo $fileUnquoted variable can split words.echo "$file"
rm "$file" without testingDestructive loop with no preview.Echo first, then delete once verified.
Using arrays with shArrays are Bash-specific.Use #!/usr/bin/env bash.
Try it yourself

Practice exercises

  1. Loop through three service names and print each one.
  2. Loop through all .txt files and print their size with ls -lh.
  3. Loop through a list of domains and run dig +short.
  4. Loop through log files and count lines with wc -l.
Related

Keep learning Bash

Safer loops

Safer file loops with spaces in filenames

Simple loops are fine for controlled examples, but real filenames can contain spaces. Quote variables and prefer predictable input.

for file in ./*.log; do
  [ -e "$file" ] || continue
  echo "Checking: $file"
  wc -l "$file"
done
Example output:
Checking: ./access log.log
1842 ./access log.log
Checking: ./error.log
91 ./error.log
Next steps

Where for loops fit in Bash scripting

Use for loops when you already have a list. Use while loops when you need to keep going while a condition is true. For reusable logic, move repeated actions into Bash functions.

FAQ

Frequently Asked Questions

When should I use a Bash for loop?

Use a for loop when you want to repeat commands over a known list of items, such as files, users, domains or arguments.

How do I loop through files safely in Bash?

Quote the variable inside the loop and check that a glob matched before processing it.

What is the difference between for and while loops?

A for loop works through a list. A while loop repeats while a condition remains true.

Can a for loop use command output?

Yes, but be careful with spaces and newlines. For line-based input, a while read loop is often safer.

$ practise_next --topic bash

Practise this next

Turn the guide into practice with a related quiz, builder, cheat sheet or learning path.