7.2 Looping Constructs for Files and Input
Key Takeaways
- for loops iterate word lists, globs, ranges, or command substitution results—ideal for acting on many files or names in EX200 scripts.
- while loops repeat as long as a condition or read pipeline succeeds; they are the right tool for line-oriented input and counters.
- Combine loops with if/test from Section 7.1 so each iteration can skip, log, or fail cleanly without aborting the whole system.
- Quote variables in loops; for f in $list without quotes breaks on spaces—prefer for f in "$dir"/* patterns carefully or while read -r.
- Keep exam scripts short: loop, check, act, exit—avoid complex nested control flow that is hard to debug under time pressure.
7.2 Looping Constructs for Files and Input
Quick Answer: Use
forto walk a list (files, names, numbers) andwhileto repeat while a condition holds or whilereadreturns lines. Inside the loop body, useif/testto decide what to do per item. Endfor/whilewithdone. Keep scripts executable with#!/bin/bashandchmod +x.
Where loops fit on EX200
Conditional execution decides once. Looping constructs decide for each of many items—exactly what admins automate:
- Apply a fix to every
.confunder a directory - Create several users or directories from a list
- Parse each line of a report or
stdin - Retry or poll until a condition clears (with care on timed exams)
Official wording emphasizes simple scripts. Graders want correct end state and a working script, not clever golf.
for loops: the workhorse
List form
#!/bin/bash
for name in alice bob carol; do
echo "processing $name"
done
Words after in are the iteration values. do … done is the body.
Glob form (files)
#!/bin/bash
for f in /etc/ssh/*.conf; do
if [ -f "$f" ]; then
echo "file: $f"
fi
done
When a glob matches nothing, bash’s default behavior may leave the pattern as a literal string (depending on nullglob). Guard with [ -f "$f" ] or [ -e "$f" ] so you do not act on a fake path named *.conf.
Safer pattern for “all regular files here”:
#!/bin/bash
DIR="/var/tmp/examdata"
for f in "$DIR"/*; do
[ -f "$f" ] || continue
echo "regular file: $f"
done
continue skips to the next iteration; break exits the loop early.
C-style numeric for (bash)
for ((i=1; i<=5; i++)); do
echo "n=$i"
done
Useful for counters and numbered files (file1 … file5). Requires #!/bin/bash, not pure POSIX sh.
Brace expansion
for n in {1..3}; do
mkdir -p /tmp/exam/dir$n
done
Brace expansion happens before the loop runs. Good for small fixed ranges in lab scripts.
Command substitution as the list
for user in $(cut -d: -f1 /etc/passwd); do
echo "$user"
done
Warning: word-splitting applies. Usernames without spaces are fine; arbitrary filenames with spaces are not safe this way. For filenames, prefer globs or while read (below).
while loops: conditions and line input
Condition form
#!/bin/bash
count=1
while [ "$count" -le 3 ]; do
echo "count=$count"
count=$((count + 1))
done
The loop continues while the condition command exits 0. Arithmetic uses $(( ... )) in bash.
until (awareness)
until [ -f /var/lock/ready ]; do
sleep 1
done
until runs until the condition succeeds. On EX200 you rarely need long waits—do not sleep in tight infinite loops during a timed exam unless the task demands waiting.
Reading lines: the admin classic
#!/bin/bash
# Process each non-empty line of a file
while IFS= read -r line; do
[ -z "$line" ] && continue
echo "LINE: $line"
done < /root/names.txt
| Piece | Role |
|---|---|
IFS= | Do not trim leading/trailing whitespace oddly; empty IFS preserves line content better for many cases |
read -r | Do not treat backslashes as escapes |
done < file | Redirect file into the loop’s stdin |
From a pipeline:
cut -d: -f1 /etc/passwd | while IFS= read -r user; do
echo "$user"
done
Exam note: a while after a pipe often runs in a subshell in bash, so variable assignments inside may not persist outside. For EX200 scripts that only print or call commands per line, that is usually fine. If you must accumulate a counter outside, prefer done < file redirection over a pipe when possible.
Reading space-separated fields
while read -r col1 col2 rest; do
echo "first=$col1 second=$col2 rest=$rest"
done < /tmp/data.txt
read splits on IFS (default whitespace).
Nesting with if and exit status
Loops without conditionals are rare in real tasks:
#!/bin/bash
# Ensure every listed path is a directory; create if missing; fail on file collision
for path in /opt/app/data /opt/app/logs /opt/app/tmp; do
if [ -d "$path" ]; then
echo "exists dir: $path"
elif [ -e "$path" ]; then
echo "ERROR: $path exists and is not a directory" >&2
exit 1
else
mkdir -p "$path" || exit 1
echo "created: $path"
fi
done
exit 0
Failing fast with exit 1 inside a loop is appropriate when continuing would leave a half-configured system.
Practical exam scripts
Bulk ownership on files matching a pattern
#!/bin/bash
# chown-web.sh — set owner for web content files
WEBROOT="/var/www/html"
OWNER="apache:apache"
if [ ! -d "$WEBROOT" ]; then
echo "Missing $WEBROOT" >&2
exit 1
fi
for f in "$WEBROOT"/*; do
[ -f "$f" ] || continue
chown "$OWNER" "$f" || exit 1
done
Process arguments in a loop (preview of §7.3)
#!/bin/bash
if [ $# -eq 0 ]; then
echo "Usage: $0 file..." >&2
exit 2
fi
for f in "$@"; do
if [ -f "$f" ]; then
wc -l < "$f"
else
echo "skip missing: $f" >&2
fi
done
"$@" preserves each argument as its own word—critical when paths have spaces.
Inventory script writing results
#!/bin/bash
OUT=/root/bin-inventory.txt
: > "$OUT" # truncate/create
for f in /usr/local/bin/*; do
[ -x "$f" ] || continue
ls -l "$f" >> "$OUT"
done
: > file is a common truncate idiom; >> appends per iteration.
Simple retry loop (use sparingly)
#!/bin/bash
tries=0
while [ "$tries" -lt 5 ]; do
if ping -c1 -W1 192.0.2.10 &>/dev/null; then
echo "reachable"
exit 0
fi
tries=$((tries + 1))
sleep 1
done
echo "unreachable" >&2
exit 1
Only when the task implies waiting. Blind infinite while true without break is an exam time sink.
Loop control keywords
| Keyword | Effect |
|---|---|
continue | Skip rest of body; next iteration |
break | Leave the loop immediately |
exit N | Leave the entire script with status N |
return | Leave a function (when you use functions) |
for f in /etc/*; do
[ -r "$f" ] || continue
grep -q ExamMarker "$f" && break
done
Infinite loop pitfalls
while true; do
...
done
Always ensure a break or exit path. On the exam, an infinite loop freezes your terminal session until Ctrl-C—know how to interrupt (Ctrl-C) if you make this mistake in testing.
Performance and simplicity
EX200 does not grade micro-optimization. Prefer clarity:
- One loop, obvious body
- Explicit
[ -f ]guards - Errors on stderr
- Non-zero exit when the task’s success criteria fail
Avoid deep nesting (for inside for inside while) unless the task forces multi-level structure.
Combining with shebang discipline
Every loop script still needs:
#!/bin/bash
# ... loops ...
chmod +x /path/to/script.sh
bash -n /path/to/script.sh
./path/to/script.sh
Syntax-check with bash -n after writing loops—missing done is a frequent typo.
Common traps
for f in $(ls dir)— breaks on spaces/newlines; use globs orfind -print0patterns carefully; for EX200, simple globs +[ -f ]are enough.- Forgetting
done— parse error. - Unquoted
$@/ unquoted globs mishandled — wrong word splits. - Assuming pipeline
whilevariables persist — subshell scoping. - No guard when glob matches nothing — acts on literal
*. - Using
exitwhen you meantbreak— aborts whole script mid-batch. - Sleeping forever waiting for a condition that never arrives.
Section checkpoint
You should write for loops over lists and file globs, while loops with numeric conditions and read -r line processing, combine them with if/test, use continue/break/exit appropriately, protect paths with quotes and existence checks, and leave short executable scripts that process many files or input lines correctly on RHEL 10.
Which loop form is most appropriate for running the same commands once for each regular file matching /data/*.log?
In bash, what do continue and break do inside a for or while loop?
Why is while IFS= read -r line; do ...; done < file.txt often preferred over for line in $(cat file.txt) for line-oriented processing?
A for loop uses for f in /opt/app/*; do without checking types. The directory is empty and nullglob is not set. What is a common bash result you must guard against?