Loops repeat work so you do not. Bash has three — for walks a list, while runs while a command succeeds, until the inverse — and one safety habit ("$@"-style quoting) keeps all of them from breaking on filenames with spaces.

for: walking a list #

Over a literal list, then over a glob
$ for svc in nginx redis aospix; do systemctl status "$svc"; done
$ for f in *.log; do gzip -9 "$f"; done

The glob form is the everyday hero — the shell expands the list, the loop handles each name. Quote the variable inside, always: unquoted "$f" breaks on spaces, per the quoting rules.

The C-style form exists for counters:

A counted loop
$ for ((i = 1; i <= 5; i++)); do echo "attempt $i"; done

while: driven by commands #

while runs as long as its command exits 0. The canonical pattern reads a file line by line:

The read loop, done safely
$ while IFS= read -r line; do
    echo "line: $line"
  done < notes.txt

IFS= preserves leading whitespace, -r stops backslash mangling, and the redirect feeds the whole loop — not a command inside it. Piping into the loop works too, with the caveat that the loop then runs in a subshell, so variables set inside vanish after.

LoopUse when
for x in listKnown list — literal, glob, or array
while read -r lineLine-by-line over a file or stream
while cmdRepeat while a check passes
until cmdRepeat until a check passes
for ((i=0;...))Counting

until and the retry #

until inverts while — the natural shape of "wait for it":

Wait for a service to answer
$ until curl -sf http://localhost:8080/health > /dev/null; do
    echo "waiting..." && sleep 2
  done

Pair it with a timeout guard so a dead service yields a script that exits rather than one that waits forever:

Bounded waiting
$ for i in {1..30}; do curl -sf ... && break || sleep 2; done

Lists belong in arrays; dispatching inside a loop is case work. The scripting series continues.