Bash offers several ways to iterate. The most basic form is for name in list; do body; done, where the list may be a literal set of words, a glob, the output of a command substitution, or an array expansion. Because unquoted globs and command substitutions are subject to word splitting, list iteration over arrays should always be written for item in "${arr[@]}", with both the subscript and the variable quoted, so that elements containing spaces or special characters are preserved correctly. A more verbose variant, the C-style for loop — for (( i=0; i<10; i++ )) — uses arithmetic expression syntax and is convenient for counting loops and parallel iteration.
Conditional loops take the form while condition; do body; done and until condition; do body; done. The condition is re-evaluated after each iteration; a while loop continues as long as the command returns zero, and an until loop continues as long as it returns non-zero — an until is essentially a negated while, handy for retry patterns like until ping -c1 host; do sleep 5; done. Inside any loop, break exits the innermost loop and continue skips to the next iteration; both accept an optional integer level, so break 2 exits two nested loops at once.
Reading a file line by line is a special case of a while loop: while IFS= read -r line; do echo "$line"; done < file.txt. Setting IFS= to an empty string preserves leading and trailing whitespace on each line, and the -r flag prevents read from interpreting backslash escapes. To read fields separated by a delimiter into separate variables, supply a custom IFS: IFS=: read -r user _ uid _ _ home _ < /etc/passwd, where the placeholder _ simply discards fields you do not care about. The same pattern adapted to "\(@" — for arg in "\)@"; do echo "$arg"; done — is the canonical way to iterate over a script's own command-line arguments.