100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Bash

Loops in Bash (for, while, until)

Learn Bash's three loop constructs — for, while, and until — plus loop control with break and continue, to iterate over lists, files, and command output safely.

Bash Scripting FundamentalsBeginner9 min readJul 9, 2026
Analogies

Loops in Bash (for, while, until)

Loops let a script repeat a block of commands over a list of items, while a condition holds true, or until a condition becomes true. Bash provides three constructs — for, while, and until — that cover essentially every iteration pattern you'll need: iterating over a fixed list or glob (for), iterating while some state remains true such as reading lines from a file (while), and the less common but occasionally useful inverse, repeating until a condition finally becomes true (until). Choosing the right one, and reading input safely inside them, is central to writing scripts that behave correctly on real, messy data.

🏏

Cricket analogy: A captain repeats a set field for every over, keeps rotating bowlers while the pitch stays helpful, or keeps trying different fields until a wicket finally falls — choosing the right approach is central to good captaincy on messy, real match days.

for loops: lists, globs, and C-style

The classic for var in list; do ...; done iterates once per word in list, where the list can be literal values, a glob like *.log, command substitution output, or an array expansion. Bash also supports a C-style for (( i=0; i<n; i++ )) form for numeric counting, which is often clearer than manual arithmetic inside a while loop when you just need to repeat something a fixed number of times.

🏏

Cricket analogy: for var in list iterating over a fixed list is like going through specific fielding positions, a glob-style "all outfielders," or a whole squad roster pulled from selection; the C-style counting loop is like simply counting down the remaining ten overs one by one, clearer than tracking overs manually.

bash
# Iterate over a glob (safe: no word-splitting issues since it's not a variable)
for logfile in /var/log/*.log; do
    echo "Processing $logfile"
    gzip "$logfile"
done

# Iterate over literal values
for name in Alice Bob Carol; do
    echo "Hello, $name"
done

# Iterate over an array, always double-quoted with @
users=("alice" "bob" "carol")
for user in "${users[@]}"; do
    id "$user" &>/dev/null || echo "$user does not exist"
done

# C-style numeric loop
for (( i=1; i<=5; i++ )); do
    echo "Attempt $i of 5"
done

while and until loops

while command; do ...; done repeats the body as long as command succeeds (exit status 0); until command; do ...; done is its mirror image, repeating as long as command fails. while read -r line; do ...; done < file is the canonical, safe way to process a file line by line in Bash — -r prevents backslash escapes from being interpreted, and redirecting the file directly (rather than piping cat file |) avoids an unnecessary subshell and preserves any variables set inside the loop after it exits.

🏏

Cricket analogy: while continuing as long as the pitch report says "batting-friendly" versus until continuing as long as it says "difficult" are mirror strategies; reading match updates ball-by-ball with -r is like taking notes literally rather than interpreting shorthand, and getting the feed directly from the stadium rather than a relay commentator avoids losing details.

bash
# Safe line-by-line file reading
while IFS= read -r line; do
    echo "Line: $line"
done < /etc/hosts

# Poll until a service responds, with a timeout
attempts=0
until curl -sf http://localhost:8080/healthz > /dev/null; do
    attempts=$((attempts + 1))
    if (( attempts >= 30 )); then
        echo "Service did not become healthy in time" >&2
        exit 1
    fi
    sleep 2
done
echo "Service is healthy"

# Infinite loop with an explicit break condition
while true; do
    read -rp "Continue? [y/n] " answer
    [[ "$answer" == "n" ]] && break
done

Piping into a while loop, e.g. cat file | while read -r line; do count=$((count+1)); done, runs the loop in a subshell because it's the right side of a pipe. Any variables modified inside the loop (like count) are lost once the subshell exits, silently producing wrong results. Prefer input redirection (while read -r line; do ...; done < file) or process substitution (done < <(command)) so the loop runs in the current shell.

IFS= before read prevents leading/trailing whitespace from being stripped from each line, and -r stops backslashes from being treated as escape characters — together while IFS= read -r line is the idiomatic, safest way to read arbitrary lines, including ones with trailing spaces or embedded backslashes, without silent data mangling.

break and continue

break exits a loop immediately; continue skips the rest of the current iteration and moves to the next one. Both accept an optional numeric argument (break 2, continue 2) to affect an outer loop when loops are nested, which is occasionally useful but can hurt readability if overused — extracting the inner loop into a function is often clearer.

🏏

Cricket analogy: break is a captain immediately ending the over after a controversial no-ball dispute; continue is skipping to the next ball without dwelling on a dot ball; break 2 would end both the over and the innings segment at once, though it's clearer to hand that decision to a dedicated review process.

bash
for file in *.csv; do
    [[ -s "$file" ]] || continue   # skip empty files
    if [[ "$file" == "STOP.csv" ]]; then
        break
    fi
    process_csv "$file"
done
  • for var in list; do ...; done iterates over words, globs, command output, or array expansions.
  • C-style for (( ; ; )) loops are convenient for numeric counting with explicit init/condition/increment.
  • while repeats as long as its test command succeeds; until repeats as long as its test command fails.
  • while IFS= read -r line; do ...; done < file is the safe idiom for reading a file line by line without word mangling.
  • Piping into a while loop runs it in a subshell, silently discarding variable changes made inside; use redirection instead.
  • break and continue control loop flow and can target an outer loop with a numeric argument when nested.

Practice what you learned

Was this page helpful?

Topics covered

#Bash#LinuxShellScriptingStudyNotes#DevOps#LoopsInBashForWhileUntil#Loops#While#Until#Lists#StudyNotes#SkillVeris