3.3b Loops, Exit Status, and Simple Conditionals

Key Takeaways

  • A for loop repeats commands once per item in a list: for item in a b c; do …; done
  • Every command sets an exit status; $? holds the status of the last command (0 usually means success)
  • Non-zero exit status indicates failure or an error condition the script can test
  • Simple if / then / else / fi and the test/[ ] command let scripts branch on success, failure, or string/file checks
  • Combine loops with echo and positional parameters to process several files or arguments automatically
Last updated: July 2026

Loops, Exit Status, and Simple Conditionals

Once a script can print messages and read $1 / $@, the next Essentials skill is repeating work and reacting to success or failure. Objective 3.3 expects familiarity with for loops, exit status ($?), and simple conditionals—enough to read and write short examples, not to build production frameworks.

for loops: do something for each item

A Bash for loop runs a block once for every word in a list:

#!/bin/bash
for city in Tokyo Nairobi Lima
do
  echo "Visiting $city"
done
KeywordRole
forStarts the loop and names the loop variable
inIntroduces the list of values
dodoneCommands that run on each iteration

Each pass sets city to the next word, then runs the body. Output:

Visiting Tokyo
Visiting Nairobi
Visiting Lima

Loop over script arguments with "$@":

#!/bin/bash
for arg in "$@"
do
  echo "Argument: $arg"
done

Loop over files matching a glob (Essentials-level pattern):

#!/bin/bash
for f in *.txt
do
  echo "Found $f"
done

If no *.txt files exist, Bash may leave the pattern literal depending on shell options—another reason to keep examples small and tested. On the exam, recognize the for … in …; do …; done shape and that the loop variable expands with $ inside the body.

You will also see a one-line form with semicolons:

for n in 1 2 3; do echo "n=$n"; done

Same structure—just compacted. Essentials does not require C-style for ((i=0; …)) loops.

Exit status and $?

Every command returns an exit status (exit code)—a small integer. By convention:

Exit statusMeaning
0Success (true / OK)
Non-zero (1–255)Failure or error

Immediately after a command, $? expands to that status:

ls /tmp
echo "ls exit status was $?"

ls /no/such/path
echo "failed ls exit status was $?"

A successful ls typically prints 0. A failed ls prints a non-zero value (often 2 for a serious usage/path error—do not memorize every code; memorize 0 = success, non-zero = failure).

Critical timing: $? updates after every command. If you run:

false
echo "status=$?"
echo "now $?"

the second echo reports 0 because the first echo itself succeeded. Capture status early when you need it:

cp important.dat /backup/
status=$?
echo "copy status was $status"

Built-ins useful in demos: true always exits 0; false always exits non-zero (usually 1).

Simple conditionals: if, then, else, fi

Scripts branch with if:

#!/bin/bash
if grep -q "error" /var/log/app.log
then
  echo "Found an error line"
else
  echo "No error line matched"
fi

if looks at the exit status of the command that follows. Status 0 takes the then branch; non-zero takes else (if present). End the construct with fi (if spelled backwards).

The test command (also spelled []) performs checks and returns an exit status:

#!/bin/bash
if [ -f "$1" ]
then
  echo "File exists: $1"
else
  echo "Missing: $1"
fi
Test exampleTrue when
[ -f file ]file exists and is a regular file
[ -d dir ]dir exists and is a directory
[ -z "$1" ]$1 is empty
[ "$a" = "$b" ]Strings are equal
[ $# -eq 2 ]Exactly two arguments

Spaces inside [ ] matter: [ -f "$1" ] is correct; [-f "$1"] is not. Quote expansions in tests.

Checking exit status explicitly:

#!/bin/bash
ping -c 1 "$1" >/dev/null 2>&1
if [ $? -eq 0 ]
then
  echo "Host reachable"
else
  echo "Host not reachable"
fi

Or, more idiomatically, put the command directly after if so you do not need a separate $? line.

Reading $? without fooling yourself

Candidates often print $? too late. Any command—including echo—updates the status. After a failing cp, run your status check or save $? into a variable before you run unrelated helpers. Otherwise you may report success only because the message printer succeeded.

Also know that pipelines and redirections still leave an exit status you can test. Essentials will not demand deep pipeline status rules (pipefail and friends); it will ask whether zero means success and which expansion holds the last status.

Combining loops and status

#!/bin/bash
for host in "$@"
do
  if ping -c 1 "$host" >/dev/null 2>&1
  then
    echo "$host OK"
  else
    echo "$host FAIL"
  fi
done

This Essentials-sized pattern: iterate arguments with for, test success with if, report with echo. You do not need while, case, functions, or set -e for this exam tier—recognize them later in LPIC study if you continue.

while awareness (keep it light)

Objective lists sometimes mention while alongside for. At Essentials level, know the shape:

#!/bin/bash
count=1
while [ "$count" -le 3 ]
do
  echo "count=$count"
  count=$((count + 1))
done

while repeats as long as the tested command succeeds (exit 0). Prefer for when you already have a fixed list of words or "$@"; use while when you keep going until a condition flips. Do not over-invest in infinite-loop patterns or advanced arithmetic—recognize the keywords and the shared do/done body.

Choosing for vs a one-shot if

Use if when you decide once: does this file exist? did this command succeed? Use for when the same decision or action must run for each item in a list—each argument, each *.log file, each hostname. Many short Essentials scripts are just “loop the arguments, test something, echo a result.”

Another small pattern worth recognizing is testing the argument count before you begin real work:

#!/bin/bash
if [ "$#" -lt 1 ]
then
  echo "Usage: $0 filename"
  exit 1
fi
echo "Processing $1"

Here [ "$#" -lt 1 ] is true when fewer than one argument arrived. The script prints a usage hint and exits non-zero so callers can detect failure. That ties $#, test/[, if, and exit status together without leaving Essentials scope.

What “success vs failure” means on the exam

Questions may ask what $? shows after a command, whether 0 means success, or which branch runs when grep finds no match (non-zero → else). Connect the ideas: commands return status → $? reads it → if / test branch on it → for (or simple while) repeats the pattern across a list. Fill-in items love the tokens $?, fi, and done—type them exactly. When options mix loop keywords with unrelated tools, pick the construct that matches the stem: repeating a list points to for/do/done; branching on success points to if/then/fi and exit status 0.

Test Your Knowledge

Immediately after a command fails, which expansion shows that command’s exit status?

A
B
C
D
Test Your Knowledge

By convention, which exit status indicates that a command succeeded?

A
B
C
D
Test Your Knowledge

Which snippet correctly loops over the words red green blue and prints each one?

A
B
C
D