Table of Contents
Understanding Conditionals in Shell Scripts
Conditionals let your script make decisions: “if this is true, do that; otherwise, do something else.” In shell scripting, this is mainly done with the if statement and test expressions.
This chapter assumes you already know how to write and run basic scripts and use variables.
Basic `if` Syntax
The basic form:
if COMMANDS_OR_TEST; then
# commands if condition is true
fiExample:
#!/bin/bash
if [ -f "/etc/passwd" ]; then
echo "/etc/passwd exists"
fiKey points:
ifruns the command (or test) after it.- If that command succeeds (exit status 0), the
thenblock runs. fiends theif(it’sifbackwards).
Using `then` on Same or Next Line
Two equivalent styles:
if [ "$USER" = "root" ]; then
echo "You are root"
fiif [ "$USER" = "root" ]
then
echo "You are root"
fiBoth are valid; choose one style and be consistent.
Exit Status and Truth in Shell
In shell conditionals:
- Exit status
0means true/success. - Any non‑zero status means false/failure.
This is backwards from many other languages.
You can use any command as a condition:
if ping -c 1 example.com >/dev/null 2>&1; then
echo "Network looks OK"
else
echo "Cannot reach example.com"
fi
Here ping itself is the test; no [ ] involved.
`if … else` and `if … elif … else`
`if … else`
if [ "$USER" = "root" ]; then
echo "You are root"
else
echo "You are not root"
fi`if … elif … else`
Use elif (else-if) for multiple branches:
if [ "$HOUR" -lt 12 ]; then
echo "Good morning"
elif [ "$HOUR" -lt 18 ]; then
echo "Good afternoon"
else
echo "Good evening"
fiEvaluation order:
- First condition that is true runs its block.
- Remaining
elif/elseparts are skipped.
Testing Conditions with `[` and `test`
Most shell scripts use the [ (also called test) command in if:
if [ condition ]; then
...
fiImportant:
[is a command, not syntax.- You must have:
- A space after
[ - A space before
]
Example:
if [ "$ANSWER" = "yes" ]; then
echo "You answered yes"
fi
Wrong (no spaces): if ["$ANSWER"="yes"]; then → will fail.
test is equivalent:
if test "$ANSWER" = "yes"; then
echo "You answered yes"
fiString Comparisons
Common string tests with [ … ]:
- Equality:
[ "$a" = "$b" ]or[ "$a" == "$b" ] - Inequality:
[ "$a" != "$b" ] - Non-empty string:
[ -n "$a" ](true if length > 0) - Empty string:
[ -z "$a" ](true if length = 0)
Examples:
if [ "$USER" = "root" ]; then
echo "Running as root"
fi
if [ -z "$NAME" ]; then
echo "NAME is empty"
else
echo "Hello, $NAME"
fi
Always quote string variables ("$VAR") to avoid errors when they are empty or contain spaces.
Numeric Comparisons
For integers, use these operators with [ … ]:
-eq: equal-ne: not equal-lt: less than-le: less than or equal-gt: greater than-ge: greater than or equal
Example:
if [ "$AGE" -lt 18 ]; then
echo "You are under 18"
elif [ "$AGE" -ge 65 ]; then
echo "You are 65 or older"
else
echo "You are between 18 and 64"
fi
Do not use < or > inside [ … ] for numbers; those are for string ordering and will be treated specially by the shell (like redirection).
File Tests
The [ … ] command has many tests for files:
Common ones:
-e FILE: exists-f FILE: exists and is a regular file-d DIR: exists and is a directory-r FILE: readable-w FILE: writable-x FILE: executable-s FILE: exists and size > 0
Examples:
FILE="/etc/passwd"
if [ -e "$FILE" ]; then
echo "$FILE exists"
fi
if [ -f "$FILE" ]; then
echo "$FILE is a regular file"
fi
if [ -r "$FILE" ]; then
echo "$FILE is readable"
fiDirectory example:
DIR="/var/log"
if [ -d "$DIR" ]; then
echo "$DIR is a directory"
fiCombining Conditions: `!`, `-a`, `-o`, and `&&`/`||`
Negation with `!`
! in front of a test inverts its result:
if [ ! -f "/tmp/test.txt" ]; then
echo "File does not exist"
fiLogical AND / OR inside `[ … ]`
Some shells support:
-a: AND-o: OR
Example (not recommended for new scripts):
if [ -f "$FILE" -a -r "$FILE" ]; then
echo "File exists and is readable"
fi
These can behave differently across shells and can be confusing. Prefer using && and || with separate tests.
Using `&&` (AND) and `||` (OR)
You can chain full commands:
cmd1 && cmd2→ runcmd2only ifcmd1succeeded.cmd1 || cmd2→ runcmd2only ifcmd1failed.
In an if:
if [ -f "$FILE" ] && [ -r "$FILE" ]; then
echo "File exists and is readable"
fi
if [ "$ANSWER" = "yes" ] || [ "$ANSWER" = "y" ]; then
echo "You agreed"
fi
This style is clearer and more portable than -a and -o.
`[` vs `[[` (Extended Test)
Many modern shells (including bash) support [[ … ]], which is more powerful and safer than [ … ]. It is not POSIX-standard, but is common in bash scripts.
Key differences:
[[ … ]]handles word splitting and globbing more safely.- Pattern matching with
=and==supports*,?, etc. directly. - You can use
&&and||inside it naturally.
Examples (bash-only):
#!/bin/bash
if [[ "$ANSWER" == y* ]]; then
echo "You answered with something starting with 'y'"
fi
if [[ "$USER" == "root" || "$USER" == "admin" ]]; then
echo "Privileged user"
fi
Inside [[ … ]], you usually don’t need to quote variables for simple string comparisons, but quoting is still good practice for consistency.
Using Command Results in Conditions
Instead of testing only variables or files, you can test whether commands succeed.
Example: check if a directory can be created:
if mkdir /tmp/testdir 2>/dev/null; then
echo "Created directory"
else
echo "Failed to create directory"
fi
mkdir is the condition. If it returns exit code 0 (success), the then block runs.
Example: checking if a user exists (using id):
if id "alice" >/dev/null 2>&1; then
echo "User alice exists"
else
echo "User alice does not exist"
fi
Redirecting output to /dev/null keeps the terminal clean while still using the command’s exit status.
`case` as an Alternative to Multiple `if`s (Briefly)
For multiple specific values of one variable, case is often cleaner than many elif branches:
#!/bin/bash
echo -n "Choose an option (start/stop/restart): "
read ACTION
case "$ACTION" in
start)
echo "Starting service..."
;;
stop)
echo "Stopping service..."
;;
restart)
echo "Restarting service..."
;;
*)
echo "Unknown option: $ACTION"
;;
esac
The case statement is another form of conditional, especially useful for menus. Its detailed use is typically covered in a more advanced scripting chapter.
Common Pitfalls with Conditionals
Some frequent problems and how to avoid them:
- Forgetting spaces with
[and]: - Wrong:
if ["$X"="5"]; then - Right:
if [ "$X" = "5" ]; then - Using
=for numbers instead of-eq:
# Numeric comparison: use -eq, -lt, -gt, etc.
if [ "$N" -eq 10 ]; then
echo "N is ten"
fi- Not quoting variables:
# Wrong: unquoted, can break if NAME is empty or has spaces
if [ $NAME = "Bob" ]; then
# Right:
if [ "$NAME" = "Bob" ]; then- Using single
=instead of==in[[ … ]]?
In [[ … ]], both = and == work for string comparison, so this is usually fine. In [ … ], stick with = (POSIX).
- Confusing
[and[[syntax:
Don’t mix them on the same line:
# Use one style:
if [ "$X" = "1" ] && [ "$Y" = "2" ]; then
...
fi
# Or:
if [[ "$X" = "1" && "$Y" = "2" ]]; then
...
fiSmall Practical Examples
Check if a directory is writable before writing
#!/bin/bash
TARGET_DIR="/tmp"
if [ -d "$TARGET_DIR" ] && [ -w "$TARGET_DIR" ]; then
echo "Writing file..."
echo "Hello" > "$TARGET_DIR/hello.txt"
else
echo "Cannot write to $TARGET_DIR"
fiSimple yes/no prompt
#!/bin/bash
read -p "Do you want to continue? [y/N] " ANSWER
if [[ "$ANSWER" == "y" || "$ANSWER" == "Y" ]]; then
echo "Continuing..."
else
echo "Aborting."
fiConditionals are central to making your scripts react to different situations. Practice by:
- Checking user input and responding differently.
- Verifying files before using them.
- Running commands only when preconditions are met.
In later scripting chapters, you’ll combine conditionals with loops and functions to build more powerful scripts.