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.
Last updated: August 2026

7.2 Looping Constructs for Files and Input

Quick Answer: Use for to walk a list (files, names, numbers) and while to repeat while a condition holds or while read returns lines. Inside the loop body, use if/test to decide what to do per item. End for/while with done. Keep scripts executable with #!/bin/bash and chmod +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 .conf under 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. dodone 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 (file1file5). 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
PieceRole
IFS=Do not trim leading/trailing whitespace oddly; empty IFS preserves line content better for many cases
read -rDo not treat backslashes as escapes
done < fileRedirect 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

KeywordEffect
continueSkip rest of body; next iteration
breakLeave the loop immediately
exit NLeave the entire script with status N
returnLeave 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

  1. for f in $(ls dir) — breaks on spaces/newlines; use globs or find -print0 patterns carefully; for EX200, simple globs + [ -f ] are enough.
  2. Forgetting done — parse error.
  3. Unquoted $@ / unquoted globs mishandled — wrong word splits.
  4. Assuming pipeline while variables persist — subshell scoping.
  5. No guard when glob matches nothing — acts on literal *.
  6. Using exit when you meant break — aborts whole script mid-batch.
  7. 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.

Test Your Knowledge

Which loop form is most appropriate for running the same commands once for each regular file matching /data/*.log?

A
B
C
D
Test Your Knowledge

In bash, what do continue and break do inside a for or while loop?

A
B
C
D
Test Your Knowledge

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
B
C
D
Test Your Knowledge

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?

A
B
C
D