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.
7.1 Conditional Execution: if, test, and []
Quick Answer: Create a bash script with
#!/bin/bash,chmod +x script.sh, then branch withif condition; then ... elif ... else ... fi. Conditions are usuallytest/[ ... ](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.
| Shebang | When to use |
|---|---|
#!/bin/bash | Preferred for bash features and exam scripts |
#!/usr/bin/env bash | Portable labs; also fine if bash is on PATH |
#!/bin/sh | POSIX 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
| Status | Meaning in if |
|---|---|
0 | Success / “true” branch |
| Non-zero | Failure / “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:
thenis required after the condition (same line after;or on the next line).ficloses everyif(notendor}).elifchains alternatives; only one branch runs.- Redirect errors with
>&2so 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
| Expression | True when |
|---|---|
-e PATH | Path exists (any type) |
-f PATH | Regular file |
-d PATH | Directory |
-L PATH / -h PATH | Symbolic link |
-r PATH | Readable by effective UID |
-w PATH | Writable |
-x PATH | Executable |
-s PATH | Exists and size > 0 |
-nt / -ot | Newer 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
| Expression | True when |
|---|---|
-z STR | String length is zero |
-n STR | String length is non-zero |
STR1 = STR2 | Equal (POSIX; bash also accepts == inside [ on many systems) |
STR1 != STR2 | Not 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):
| Operator | Meaning |
|---|---|
-eq | equal |
-ne | not equal |
-lt | less than |
-le | less or equal |
-gt | greater than |
-ge | greater 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
[ -f $file ]without quotes when$fileis empty or has spaces.- Using
=vs-eqwrong — strings vs integers. - Writing
if [ $a > $b ]—>redirects; use-gt. - Forgetting
fi— script fails parse. - Shebang wrong or missing
chmod +xwhen the grader runs./name. - Assuming non-zero always means “false file test” — any command can fail for other reasons; read stderr.
- 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.
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?
Which condition correctly tests whether the first script argument is a non-empty string that names an existing regular file?
In an if statement, when does the then branch run?
Why is if [ "$count" -gt 10 ]; then preferred over if [ "$count" > 10 ]; then for comparing integers?