Bash Loops for DevOps
In the previous article we used conditionals so scripts could branch based on system state. In DevOps work, many tasks do not run only once: checking multiple servers, reading logs line by line, retrying a command that may fail temporarily, or processing batches of configuration files.
Loops in Bash let you repeat a block of commands over a list, while a condition is true, or until a condition becomes true. This article covers for, while, until, break, continue, and practical examples that are close to daily operations work.
for in — iterate over a list
According to the GNU Bash Manual, the for name in words; do ...; done form expands the list of words, then runs the command block once for each item. On each iteration, the variable name receives the current value.
Common syntax:
| |
Example — quickly check multiple hosts with ping:
| |
The important part is using "${HOSTS[@]}" instead of bare ${HOSTS[@]}. This quoting keeps each array element intact, even when a value contains spaces.
C-style for — loop with a counter
Bash also supports for (( expr1; expr2; expr3 )), similar to the C language. This form is useful when you need an explicit counter: run exactly N times, number retry attempts, or generate filenames by index.
| |
Example — create 3 demo log files for a test environment:
| |
Inside (( ... )), you can use familiar arithmetic operators such as <=, ++, and +=. Variable names do not need a leading $ there.
while — loop while a condition is true
while runs a command block as long as the test command returns exit code 0. It is a good choice when you do not know the number of iterations in advance: reading a file to the end, waiting for a service to become ready, or polling an endpoint.
Syntax:
| |
Example — read a log file line by line and filter error lines:
| |
IFS= read -r LINE is the recommended pattern when reading lines:
IFS=prevents Bash from trimming leading or trailing whitespace.read -rpreserves\characters instead of treating them as escapes.done < "${LOG_FILE}"feeds the file into the loop’s stdin, avoiding an unnecessarycat.
until — loop until a condition becomes true
until is close to while, but the logic is reversed: it runs the command block while the test command returns non-zero, and stops when the condition returns 0.
Syntax:
| |
Example — wait for a healthcheck endpoint to become ready:
| |
until reads naturally in “repeat until success” situations. If your team finds while ! command; do ... easier to understand, using while is also completely fine.
break and continue
break exits the current loop. continue skips the rest of the current iteration and moves to the next one. According to the GNU Bash Manual, both can be used in for, while, until, and select.
Example — skip non-.log files and stop when a log is too large:
| |
The line [[ -e "${FILE}" ]] || continue handles the case where the glob /var/log/*.log matches no files. Without it, Bash may keep the pattern as a literal string, so you should check that the file exists before processing it.
DevOps practice: iterate over an SSH server list
This example reads a server list from a file, connects to each host with SSH, and runs a short check command. The server names, user, and command use generic placeholders and are not tied to a real environment.
Create servers.txt:
| |
Create check_servers.sh:
| |
Try it:
| |
A few notable points:
BatchMode=yesprevents SSH from asking for a password or passphrase in automation. If the key is not ready, the command fails instead of hanging the script.ConnectTimeout=5avoids waiting too long when a host does not respond.- Blank lines and comment lines starting with
#are skipped withcontinue. - Do not hardcode sensitive users: the script reads
SSH_USERfrom an environment variable and defaults todeploy.
DevOps practice: retry a failed command
In operations, some failures are temporary: unstable network, an unresponsive registry, or an endpoint that has just restarted. Loops let you retry in a controlled way instead of rerunning commands manually.
| |
Here, curl -f returns an error for HTTP status codes 4xx/5xx, while -sS keeps output quiet but still prints errors on failure. The final exit code tells a pipeline whether it should continue or stop.
Common mistakes
- Looping over
lsoutput:for FILE in $(ls)breaks easily when filenames contain spaces or special characters. Use a glob (for FILE in *.log) orfind ... -print0for more complex cases. - Forgetting to quote variables: Always use
"${VAR}", especially for paths, hostnames, or data read from files. - Using a pipe that loses variables outside the loop:
cat file | while read ...often runs the loop in a subshell, so variable changes inside it may disappear after the loop. Preferwhile ...; done < file. - Infinite loops without a stopping condition: When using
while true, always includebreak, a timeout, or a maximum attempt count. - Not handling blank lines or comments: When reading server lists or config files, skip blank lines and comments to make scripts more robust.
Implementation notes
- When applying this to your own project, first identify whether the loop is driven by a list or by state:
- Existing list → usually use
for. - Read line by line or wait while a condition remains true → usually use
while. - Wait until a command succeeds → consider
until.
- Existing list → usually use
- Best practices:
- Use
IFS= read -rwhen reading files line by line. - Quote arrays as
"${ARRAY[@]}"when iterating over lists. - For retry logic, always define clear
MAX_RETRIESandSLEEP_SECONDSvalues. - For SSH automation, add timeouts and avoid interactive prompts.
- Use
- Troubleshooting:
- Loop does not run? → Check whether the input list is empty or whether the glob matches any files.
- Script stops halfway with
set -e? → A command inside the loop returned non-zero without being wrapped inif,||, or explicit exit-code handling. - Reading a file loses whitespace? → Make sure you use
IFS= read -r LINE.
🎯 Conclusion
Loops are the foundation that moves Bash from one-off scripts to real automation: handling many servers, many files, many log lines, and multiple retry attempts. When you combine for, while, and until with the conditionals from the previous article, you can already write small but useful DevOps scripts for checks, deployments, and daily operations.
In the next article, we will cover file handling in Bash: reading, writing, redirecting output, using tee, find, xargs, and safer patterns for managing logs and configuration files. 🚀
References
- GNU Bash Manual — Looping Constructs — Official documentation for
for,while, anduntil. - GNU Bash Manual — Bourne Shell Builtins — Official documentation for
breakandcontinue. - GNU Bash Manual — Bash Builtin Commands — Official documentation for
read,mapfile, and other builtins. - ShellCheck Wiki — SC2045 — Why you should not loop over
lsoutput.
