Table of Contents
Why Loops Matter in Shell Scripts
Loops let your script repeat actions without copying the same commands over and over. Instead of manually writing a command 10 times, you write it once and tell the shell to repeat it.
In shell scripting, the most common loops are:
forloopswhileloopsuntilloops
bash also supports select loops (for menus), which you’ll see briefly at the end.
You already know how to run commands in a script and use variables/conditionals from previous sections; here you’ll see how to repeat blocks of commands.
`for` Loops
for loops iterate over a list of values. For each value, the loop body (the commands inside) runs once.
Basic `for` Loop Over a List
General form:
for var in item1 item2 item3; do
commands using "$var"
doneExample:
#!/bin/bash
for fruit in apple banana cherry; do
echo "I like $fruit"
doneOutput:
I like apple
I like banana
I like cherryKey points:
fruitis just a variable name; you choose it.inintroduces the list the loop will iterate over.- Each loop iteration sets
fruitto the next item in the list.
`for` Loop Over Command-Line Arguments (`$@`)
$@ represents all arguments given to the script.
#!/bin/bash
echo "You passed $# arguments."
for arg in "$@"; do
echo "Argument: $arg"
doneIf you run:
./myscript.sh one "two words" threeYou get:
You passed 3 arguments.
Argument: one
Argument: two words
Argument: three
Notice the quotes: "$@" preserves spaces in each argument as a single item.
`for` Loop Over Files (Globbing)
You often loop over files matching a pattern using wildcards (covered in the wildcards chapter):
#!/bin/bash
for file in *.txt; do
echo "Processing $file"
done*.txtexpands to all.txtfiles in the current directory.- The loop runs once per file.
Handling “no matches” cases
If there are no .txt files, many shells (including bash by default) will keep .txt as the literal string ".txt".
A simple check:
for file in *.txt; do
[ -e "$file" ] || { echo "No .txt files found"; break; }
echo "Processing $file"
done
[ -e "$file" ] checks whether the file exists; if not, we print a message and break (stop the loop).
C-Style `for` Loop (Numeric Loops)
bash supports a C-like syntax for numeric loops:
for (( i=1; i<=5; i++ )); do
echo "i is $i"
doneBreakdown:
- Initialization:
i=1 - Condition:
i<=5(loop continues while this is true) - Update:
i++(increment by 1 each iteration)
You can change the step:
for (( i=0; i<=10; i+=2 )); do
echo "Even number: $i"
doneThis is very convenient for fixed numeric ranges.
Using `seq` for Ranges (Portable Style)
On many systems, seq is available and works in more shells than C-style loops:
for i in $(seq 1 5); do
echo "Number: $i"
doneWith step:
for i in $(seq 0 2 10); do
echo "Step 2: $i"
done
Be cautious: $(...) splits on whitespace, so it’s best for simple numeric lists, not arbitrary strings with spaces.
`while` Loops
while loops run as long as a condition (a command) returns success (exit status 0). You’ll combine them with tests and commands you already know.
General form:
while condition_command; do
commands
done
The shell runs condition_command; if it succeeds, it runs the body and then tests the condition again.
Basic Counter with `while`
Example:
#!/bin/bash
count=1
while [ "$count" -le 5 ]; do
echo "Count is $count"
count=$(( count + 1 ))
doneKey points:
[ "$count" -le 5 ]is a test; if true, the loop continues.count=$(( count + 1 ))uses arithmetic expansion.
`while` Loop Reading User Input
while is often used for reading input repeatedly.
#!/bin/bash
while true; do
read -p "Enter a word (or 'quit' to exit): " word
[ "$word" = "quit" ] && break
echo "You typed: $word"
done
echo "Goodbye!"Notes:
while truecreates an infinite loop;breakstops it when needed.readgets user input line by line.
`while` Loop Reading from a File
Common pattern: process a file line by line.
#!/bin/bash
file="names.txt"
while IFS= read -r line; do
echo "Name: $line"
done < "$file"Breakdown:
IFS=prevents trimming leading/trailing whitespace.-rprevents backslash escaping.< "$file"redirects the file into the loop.lineholds each line in turn, even if it contains spaces.
`until` Loops
until is like while, but runs until the condition becomes true (i.e., it loops while the condition is false).
General form:
until condition_command; do
commands
doneThat means:
whileruns while condition is true.untilruns while condition is false.
Example:
#!/bin/bash
count=1
until [ "$count" -gt 5 ]; do
echo "Count is $count"
count=$(( count + 1 ))
done
This produces the same output as the earlier while example, but the logic is reversed.
until can be handy when you think in terms of “keep going until X happens”, for example:
until ping -c 1 example.com >/dev/null 2>&1; do
echo "Waiting for example.com to respond..."
sleep 2
done
echo "example.com is reachable!"
This loop keeps running until ping succeeds.
Controlling Loop Flow: `break` and `continue`
Inside any loop (for, while, or until), you can change control flow:
breakexits the loop immediately.continueskips to the next iteration of the loop.
Using `break`
Stop the loop when a condition is met:
#!/bin/bash
for num in 1 2 3 4 5; do
if [ "$num" -eq 3 ]; then
echo "Stopping at $num"
break
fi
echo "Number: $num"
done
echo "Loop finished."Output:
Number: 1
Number: 2
Stopping at 3
Loop finished.Using `continue`
Skip just the current iteration:
#!/bin/bash
for num in 1 2 3 4 5; do
if [ "$num" -eq 3 ]; then
echo "Skipping $num"
continue
fi
echo "Number: $num"
doneOutput:
Number: 1
Number: 2
Skipping 3
Number: 4
Number: 5Nested Loops
You can place loops inside loops. Be careful with indentation so it stays readable.
Example: simple “table” of pairs:
#!/bin/bash
for i in 1 2 3; do
for j in a b c; do
echo "i=$i, j=$j"
done
doneOutput:
i=1, j=a
i=1, j=b
i=1, j=c
i=2, j=a
...
You can also use break and continue with an optional numeric argument in bash to break out of multiple levels (more advanced usage), but for now, try to keep nesting shallow.
`select` Loops (Simple Menus in Bash)
select (a bash feature) makes simple numbered menus:
#!/bin/bash
PS3="Choose a fruit (or Ctrl+C to quit): "
select fruit in apple banana cherry; do
echo "You chose: $fruit"
doneWhen you run this, you see:
1) apple
2) banana
3) cherry
Choose a fruit (or Ctrl+C to quit):- Typing
1then Enter setsfruit=appleand runs the body. - The loop repeats, asking again.
You can add conditions to exit the loop:
#!/bin/bash
PS3="Choose an option: "
select choice in list date quit; do
case "$choice" in
list)
ls
;;
date)
date
;;
quit)
echo "Goodbye!"
break
;;
*)
echo "Invalid choice."
;;
esac
done
This combines select with case (covered in conditionals).
Common Loop Pitfalls and Tips
- For strings with spaces, always quote variables:
"$var", especially in conditions or when using them as filenames. - Infinite loops:
while true; do ...; doneis fine if you have a clearbreakor external stop.- Make sure there’s a way to exit, or your script may run forever.
- Looping over command output:
- For simple numeric or word lists,
for x in $(command); do ...; donecan work. - For text with spaces or special characters, prefer a
while readloop with redirection or a pipe:
command | while IFS= read -r line; do
...
done- Performance:
- Shell loops are slower than built-in tools like
find,xargs,grep,awk, etc. For large data sets, consider using those tools, even if it means fewer loops.
Practice Ideas
Try writing small scripts using each loop type:
for:- Print “Hello” 10 times using a C-style
for. - Loop over all
.logfiles and show their sizes. while:- Ask the user for numbers until they enter
0, then print the sum. - Read a file line by line and number each line.
until:- Keep asking for a password until the user types a specific word.
- Wait until a particular file exists, then print a message.
select(inbash):- Create a simple menu to run common commands (
ls,pwd,date,exit).
These exercises will make loops feel natural and prepare you for more complex scripting tasks.