7.1 Conditional Execution: if, test, and []

Key Takeaways

  • A bash script needs a shebang (usually #!/bin/bash), executable permission (chmod +x), and is invoked by path (./script.sh) or by bash script.sh.
  • if/elif/else branches on command exit status: 0 is true/success; non-zero is false/failure—use $? to inspect status after a command.
  • test and [ ] are the classic condition helpers; [[ ]] is a bash conditional compound with safer string/pattern behavior—prefer what the task environment and man pages support.
  • Always leave spaces inside [ ... ] and quote variables: [ -f "$file" ]; bare [ -f $file ] breaks on empty values or spaces.
  • EX200 scripts are short and practical: check files/users/args, print messages, exit with meaningful status codes that survive grader checks.
Last updated: August 2026

7.1 Conditional Execution: if, test, and []

Quick Answer: Create a bash script with #!/bin/bash, chmod +x script.sh, then branch with if condition; then ... elif ... else ... fi. Conditions are usually test / [ ... ] (or bash [[ ... ]]) and any command whose exit status is 0 (true) or non-zero (false). Quote variables and put spaces inside brackets.

Why scripting is an EX200 category

Official study points list Create simple shell scripts as its own skill group. Graders look for a working script on disk that:

  • Runs under bash on RHEL 10
  • Accepts or checks inputs as described
  • Makes conditional decisions (this section)
  • Often loops and processes arguments or command output (later sections)

You are not writing large applications. You are writing short, reliable automation—often 10–40 lines—that an admin would leave in /usr/local/bin or a lab path the task names.

Anatomy of a minimal executable script

#!/bin/bash
# check-file.sh — example EX200-style script

if [ ! -f /etc/hosts ]; then
  echo "ERROR: /etc/hosts missing" >&2
  exit 1
fi

echo "/etc/hosts is present"
exit 0

Shebang

The first line #!/bin/bash (shebang) tells the kernel which interpreter runs the file when executed directly. On RHEL 10, /bin/bash is the standard interactive and scripting shell for EX200-style work.

ShebangWhen to use
#!/bin/bashPreferred for bash features and exam scripts
#!/usr/bin/env bashPortable labs; also fine if bash is on PATH
#!/bin/shPOSIX sh only—avoid bash-only syntax if you use this

Exam habit: Use #!/bin/bash unless the task forces /bin/sh.

Permissions and invocation

vim /home/student/bin/check-file.sh
chmod +x /home/student/bin/check-file.sh
/home/student/bin/check-file.sh
# or from the script directory:
./check-file.sh

Without execute bit, ./script.sh fails with “Permission denied.” You can still run bash script.sh without +x, but many tasks say “create an executable script”—chmod +x is part of the deliverable.

ls -l check-file.sh    # expect -rwxr-xr-x or similar x bits
head -1 check-file.sh  # confirm shebang

Line endings and editors

If you paste from a Windows host, ^M line endings break the shebang (/bin/bash^M). Fix with sed -i 's/\r$//' script.sh or rewrite in vim on the exam system. Prefer creating scripts on the RHEL host with vim/nano.

Exit status: the currency of conditions

Every command returns an integer status in $?:

true; echo $?     # 0
false; echo $?    # 1
ls /etc/passwd; echo $?
ls /no/such; echo $?    # non-zero
StatusMeaning in if
0Success / “true” branch
Non-zeroFailure / “false” branch

if runs a command list; success of the last command in the condition selects then vs else:

if grep -q '^root:' /etc/passwd; then
  echo "root account line exists"
else
  echo "unexpected: no root line"
fi

grep -q is silent and exits 0 on match—ideal for scripts.

Set your own status for callers and graders:

exit 0   # success
exit 1   # general failure
exit 2   # often “usage error” by convention

if / elif / else / fi syntax

#!/bin/bash
USER_NAME="$1"

if [ -z "$USER_NAME" ]; then
  echo "Usage: $0 username" >&2
  exit 2
elif id "$USER_NAME" &>/dev/null; then
  echo "User $USER_NAME exists"
  exit 0
else
  echo "User $USER_NAME does not exist"
  exit 1
fi

Rules that cost points when broken:

  1. then is required after the condition (same line after ; or on the next line).
  2. fi closes every if (not end or }).
  3. elif chains alternatives; only one branch runs.
  4. Redirect errors with >&2 so messages do not pollute stdout you might pipe later.

One-liners with && and ||

[ -d /backup ] && echo "backup dir ok" || echo "missing backup dir"
id alice &>/dev/null || useradd alice

Useful for tiny checks; for multi-step logic, prefer full if blocks for readability under exam stress.

test and [ ] — the classic conditionals

test EXPR and [ EXPR ] are equivalent interfaces to the same idea (on bash, [ is a builtin). Spaces are mandatory after [ and before ]:

# CORRECT
if [ -f /etc/hosts ]; then echo yes; fi

# WRONG — missing spaces
if [-f /etc/hosts]; then echo yes; fi

File tests you must memorize

ExpressionTrue when
-e PATHPath exists (any type)
-f PATHRegular file
-d PATHDirectory
-L PATH / -h PATHSymbolic link
-r PATHReadable by effective UID
-w PATHWritable
-x PATHExecutable
-s PATHExists and size > 0
-nt / -otNewer than / older than (two paths)
if [ -d /var/www/html ] && [ -w /var/www/html ]; then
  echo "web root is a writable directory"
fi

String tests

ExpressionTrue when
-z STRString length is zero
-n STRString length is non-zero
STR1 = STR2Equal (POSIX; bash also accepts == inside [ on many systems)
STR1 != STR2Not equal
if [ -z "$1" ]; then
  echo "missing argument" >&2
  exit 2
fi

if [ "$MODE" = "enforce" ]; then
  echo "enforcing path"
fi

Always quote "$1", "$file", "$MODE". Unquoted empty variables become missing arguments and break [ with “unary operator expected.”

Integer comparisons (use correctly)

Inside [ ], use integer operators, not < > alone (those are redirection):

OperatorMeaning
-eqequal
-nenot equal
-ltless than
-leless or equal
-gtgreater than
-gegreater or equal
COUNT=$(wc -l < /etc/passwd)
if [ "$COUNT" -gt 50 ]; then
  echo "many local users: $COUNT"
fi

[[ ]] — bash conditional compound

Bash also provides [[ ... ]] (not POSIX /bin/sh):

if [[ $name == admin* ]]; then
  echo "admin-like name"
fi

if [[ -f $config && -r $config ]]; then
  echo "config readable"
fi

Advantages often cited:

  • Safer handling of empty strings (still quote habitually)
  • && / || inside one [[ ]]
  • Pattern matching with == and =~ regex (bash)

On EX200, both [ and [[ are acceptable if the script runs under #!/bin/bash. Prefer [ if you want maximum “boring admin script” portability; use [[ when patterns simplify the task. Do not mix up closing: ]] not ].

Combining logic

# AND: both must succeed
if [ -f "$file" ] && [ -r "$file" ]; then ...

# OR: either succeeds
if [ "$user" = root ] || [ "$user" = admin ]; then ...

# Negation
if [ ! -d /mnt/data ]; then
  mkdir -p /mnt/data
fi

! negates a test. Parentheses for grouping in [ need careful escaping; keep conditions simple on the exam.

Exam-style script patterns

Pattern A: Validate argument then act

#!/bin/bash
TARGET="$1"

if [ $# -ne 1 ]; then
  echo "Usage: $0 directory" >&2
  exit 2
fi

if [ ! -d "$TARGET" ]; then
  echo "Not a directory: $TARGET" >&2
  exit 1
fi

echo "OK: $TARGET"
exit 0

$# is argument count (deep dive in Section 7.3). Using it in conditionals is standard.

Pattern B: Branch on service or package presence

#!/bin/bash
if rpm -q httpd &>/dev/null; then
  echo "httpd installed"
else
  echo "httpd missing"
  exit 1
fi

Pattern C: Numeric threshold from a command

#!/bin/bash
LOAD=$(cut -d. -f1 /proc/loadavg)
if [ "$LOAD" -ge 4 ]; then
  echo "high load: $LOAD" >&2
  exit 1
else
  echo "load ok: $LOAD"
fi

(Command substitution $() is covered fully in Section 7.4; you already need it for real scripts.)

Debugging conditionals quickly

bash -n script.sh     # syntax check only
bash -x script.sh     # trace execution

bash -n catches missing fi/then before you waste time. bash -x shows each expanded command—gold when a condition never matches.

Common traps

  1. [ -f $file ] without quotes when $file is empty or has spaces.
  2. Using = vs -eq wrong — strings vs integers.
  3. Writing if [ $a > $b ]> redirects; use -gt.
  4. Forgetting fi — script fails parse.
  5. Shebang wrong or missing chmod +x when the grader runs ./name.
  6. Assuming non-zero always means “false file test” — any command can fail for other reasons; read stderr.
  7. Editing with DOS CRLF — breaks shebang.

Relationship to later scripting sections

Conditionals alone do not loop over files or parse many arguments. Sections 7.2–7.4 add for/while, positional parameters, and command-output processing. Together they match the four official “simple shell scripts” study points.

Section checkpoint

You should write a #!/bin/bash script, mark it executable, branch with if/elif/else/fi, build file/string/integer tests with test or [ ] (and optionally [[ ]]), treat exit status 0 as success, emit clear errors on stderr, and exit with useful codes. That is conditional execution for EX200.

Test Your Knowledge

A task requires an executable bash script named /usr/local/bin/checkhosts. Which pair of steps is essential so the grader can run it as ./checkhosts or by absolute path without calling bash explicitly?

A
B
C
D
Test Your Knowledge

Which condition correctly tests whether the first script argument is a non-empty string that names an existing regular file?

A
B
C
D
Test Your Knowledge

In an if statement, when does the then branch run?

A
B
C
D
Test Your Knowledge

Why is if [ "$count" -gt 10 ]; then preferred over if [ "$count" > 10 ]; then for comparing integers?

A
B
C
D