A bash for loop (the same idea as for loop bash or for loop in bash on any Linux
box) runs a block once per item: numbers you list out, words from a command, files
that match a glob, or entries from an array. Bash actually offers two shapes: the
familiar for name in words … list form, and the C-style for (( … )) counter you
reach for when the length is numeric. The rest of this page is the syntax I use in
real bash script for loop work, with quoting called out because half the bugs in
loops are really word-splitting surprises.
Tested all the commands and code from this article on Ubuntu 25.04, kernel 6.14.0-37-generic, Bash 5.2.37.
List form: for name in …; do …; done
The loop variable takes each word produced after expansion:
for num in 1 2 3 4 5 6; do
echo "Found Element: $num"
doneOutput:
Found Element: 1
Found Element: 2
Found Element: 3
Found Element: 4
Found Element: 5
Found Element: 6Strings work the same way:
for string in STRING1 STRING2 STRING3; do
echo "Found Element: $string"
doneOutput:
Found Element: STRING1
Found Element: STRING2
Found Element: STRING3Brace expansion is handy for ranges without typing every number:
for n in {1..3}; do echo "n=$n"; doneOutput:
n=1
n=2
n=3For larger numeric ranges, seq is fine: for i in $(seq 1 10); do … (quote the
command substitution if filenames could ever appear—usually not for seq).
Arrays vs one long string
A string with spaces is still one word unless you expand an array without quotes the wrong way. Compare:
VAR=(My name is Deepak)
echo "Length array: ${#VAR[@]}"
VAR2="My name is Deepak"
echo "Length string as single element: ${#VAR2[@]}"Output:
Length array: 4
Length string as single element: 1Iterate an array safely—always quote "${VAR[@]}" so elements with spaces stay intact:
VAR=(My name is Deepak)
for word in "${VAR[@]}"; do
echo "word: $word"
doneIf you need to build an array from text, see split string into array.
C-style for (( )) (counter loops)
When the job is numeric, for (( … )) reads like C and avoids building a word list:
for (( i = 0; i <= 5; i++ )); do
echo "Iteration $i"
doneOutput:
Iteration 0
Iteration 1
Iteration 2
Iteration 3
Iteration 4
Iteration 5Two counters in one header is legal when you need coupled state:
for (( i = 1, j = 5; i <= j; i++, j-- )); do
echo "Iteration for i=$i j=$j"
doneOutput:
Iteration for i=1 j=5
Iteration for i=2 j=4
Iteration for i=3 j=3Pair this with normal branching from bash if else when each step depends on checks.
continue and break
continue skips the rest of the body and moves to the next value; break leaves the
whole loop.
Without continue, the “skip” message still falls through:
for (( i = 0; i <= 5; i++ )); do
if [ "$i" -eq 2 ]; then
echo "Don't do anything when i=2"
fi
echo "Doing something when i=$i"
doneOutput (note the duplicate line for i=2):
Doing something when i=0
Doing something when i=1
Don't do anything when i=2
Doing something when i=2
Doing something when i=3
Doing something when i=4
Doing something when i=5With continue:
for (( i = 0; i <= 5; i++ )); do
if [ "$i" -eq 2 ]; then
echo "Don't do anything when i=2"
continue
fi
echo "Doing something when i=$i"
doneOutput:
Doing something when i=0
Doing something when i=1
Don't do anything when i=2
Doing something when i=3
Doing something when i=4
Doing something when i=5break example:
for (( i = 0; i <= 5; i++ )); do
if [ "$i" -eq 2 ]; then
echo "Exiting, match successful"
break
fi
echo "Doing something when i=$i"
doneOutput:
Doing something when i=0
Doing something when i=1
Exiting, match successfulWhen the exit condition is time-based or event-based, a
while loop is often clearer than forcing a for.
One-liners and globs (skip ls in loops)
One-liner shape:
for int in alpha beta gamma; do echo "Interface: $int"; doneOutput:
Interface: alpha
Interface: beta
Interface: gammaPrefer globs or find over parsing ls. Example: print a few network sysctl
directory names (your interface list will differ):
for f in /proc/sys/net/ipv4/conf/*; do
basename "$f"
done | head -5On this machine that began with:
all
br-bf2c2e38fb2f
default
docker0
enp0s3Always quote "$f" when you pass it to other commands so odd names do not split.
Habits that keep loops boring (in a good way)
- Quote variables in tests:
[ "$i" -eq 2 ], not[ $i -eq 2 ]. - Quote
"${array[@]}"when iterating arrays. - Avoid
for f in $(find …)when filenames can contain spaces; usefind … -print0 | while IFS= read -r -d '' for an array built withmapfile. - Remember unix bash for loop semantics match Bash; POSIX
shlacks(( ))and some array features—check the shebang.
Summary
List form walks words—literals, brace ranges, globs, or "${array[@]}". Arithmetic
for ((i=0;i<n;i++)) is for counting. continue skips the rest of an iteration;
break exits the loop. Quote tests and array expansions, avoid feeding ls into for,
and use while read when find must handle spaces in paths. Those habits cover
most day-to-day loop work without surprises.
Further reading:
- GNU Bash manual — Loop constructs
- Linux commands cheat sheet for glue around loops

