Table of Contents
Why Conditionals Matter in Shell Scripts
Conditionals let your script make decisions. With conditionals, a script can choose different actions depending on user input, files that exist or not, command success or failure, and many other situations.
Without conditionals, a script would always do the same thing in the same order. With them, it can react to its environment and behave intelligently.
Exit Status and True or False
Before looking at syntax, you must understand how shells decide what is “true” and “false”.
Every command in a shell returns an exit status. By convention:
Exit status 0 means success and is treated as “true”.
Any nonzero exit status means failure and is treated as “false”.
You can see the exit status of the most recent command using the special variable $?.
For example:
ls /tmp
echo $?
ls /this/does/not/exist
echo $?
The first echo $? should print 0. The second should print a nonzero value.
Conditionals in shell scripting rely heavily on this rule. When you test something, you are usually running a small command that exits with 0 or nonzero.
The `if` Statement Basics
The basic if statement in shell scripting has this structure:
if COMMANDS; then
OTHER_COMMANDS
fi
The shell runs the COMMANDS after if. If their exit status is 0, the block after then runs. If not, it is skipped.
A simple example using an existing command:
if ls /tmp > /dev/null 2>&1; then
echo "The /tmp directory is accessible."
fi
Here, the if condition is just ls /tmp > /dev/null 2>&1. If ls succeeds, the message is printed.
Usually you do not want to type a long command for every test. Instead, shell provides the [ ] and test command to compare values and check conditions.
Testing with `[` and `test`
The [ command and the test command are equivalent. The [ form is more common in scripts.
A test has this general form:
[ expression ]The brackets are part of the command, so there must be spaces around them and around arguments.
For example:
if [ "$USER" = "root" ]; then
echo "You are root."
fi
This uses [ as a command, with arguments "$USER", =, and "root". If the comparison is true, [ exits with status 0 and the then block runs.
The same thing written with test:
if test "$USER" = "root"; then
echo "You are root."
fiInternally, both forms rely on the same rule about exit status.
String Comparisons
Common string comparisons use [ with specific operators. All of these are evaluated by the [ command, not by the shell itself.
Some typical string tests:
= checks if two strings are equal.
!= checks if two strings are not equal.
-z STRING checks if the string is empty.
-n STRING checks if the string is not empty.
Examples:
NAME="Alice"
if [ "$NAME" = "Alice" ]; then
echo "Hello Alice."
fi
if [ "$NAME" != "Bob" ]; then
echo "You are not Bob."
fi
if [ -z "$1" ]; then
echo "No argument was given."
fi
if [ -n "$1" ]; then
echo "The first argument is: $1"
fiAlways put variables in double quotes when testing strings. This avoids surprises when values contain spaces or are empty.
Numeric Comparisons
String comparison operators treat values as plain text. For numeric comparisons, use dedicated integer operators.
Common numeric operators with [:
-eq equal to
-ne not equal to
-gt greater than
-ge greater than or equal to
-lt less than
-le less than or equal to
For example:
AGE=20
if [ "$AGE" -ge 18 ]; then
echo "You are an adult."
fi
if [ "$AGE" -lt 13 ]; then
echo "You are a child."
fiThese comparisons work with integers only. Do not use them for decimal numbers.
File and Directory Tests
Shell conditionals often check properties of files and directories. The [ command includes several file test operators.
Useful file tests include:
-e FILE true if the file exists.
-f FILE true if it exists and is a regular file.
-d FILE true if it exists and is a directory.
-r FILE true if the file is readable by the current user.
-w FILE true if the file is writable.
-x FILE true if the file is executable.
For example:
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 "You can read $FILE."
fiAnd for directories:
DIR="/var/log"
if [ -d "$DIR" ]; then
echo "$DIR is a directory."
fi
These expressions also follow the exit status rule. If the condition is true, [ exits with 0.
`if...else` and `elif`
You often want one block to run when a condition is true and a different block when it is false. Use else for that.
Basic form:
if CONDITION; then
COMMANDS_IF_TRUE
else
COMMANDS_IF_FALSE
fiExample:
if [ "$USER" = "root" ]; then
echo "You have administrative privileges."
else
echo "You are a regular user."
fi
To handle more than two possibilities, use elif which means “else if”.
Structure:
if CONDITION1; then
COMMANDS1
elif CONDITION2; then
COMMANDS2
elif CONDITION3; then
COMMANDS3
else
DEFAULT_COMMANDS
fi
The shell tests each condition in order. It runs the first matching block and skips the rest. If no condition is true, it runs the else block if present.
A practical example:
if [ "$HOUR" -lt 12 ]; then
echo "Good morning."
elif [ "$HOUR" -lt 18 ]; then
echo "Good afternoon."
else
echo "Good evening."
fi
Here, only one of the messages prints, depending on the value of $HOUR.
Combining Conditions with `&&` and `||`
Many decisions depend on more than one condition at the same time. The shell lets you combine tests using logical operators && and ||.
These operators work on commands, not only on tests. They rely directly on exit status.
COMMAND1 && COMMAND2 means “run COMMAND2 only if COMMAND1 succeeds.”
COMMAND1 || COMMAND2 means “run COMMAND2 only if COMMAND1 fails.”
In conditionals, they are used to connect test expressions.
Example with [:
if [ -f "$FILE" ] && [ -r "$FILE" ]; then
echo "File exists and is readable."
fi
The first test -f "$FILE" runs. If it exits with status 0, the second test -r "$FILE" runs. Only if both are successful does the then block run.
To represent logical “or”:
if [ "$COLOR" = "red" ] || [ "$COLOR" = "blue" ]; then
echo "You chose red or blue."
fi
Only one of the two comparisons needs to succeed for the then block to run.
Because && and || work on commands, you can also chain non-test commands:
mkdir mydir && echo "Directory created successfully."
If mkdir fails, the echo is not run.
Grouping with `test` and Commands as Conditions
Any command can be used as a condition in if. You are not limited to [.
For example:
if grep -q "root" /etc/passwd; then
echo "The word 'root' is in /etc/passwd."
fi
Here, grep -q returns 0 if it finds a match and nonzero if it does not.
You can also use test directly to avoid writing brackets twice when combining with && and ||:
if test -f "$FILE" && test -w "$FILE"; then
echo "File exists and is writable."
fi
The rule remains the same. The if keyword checks the exit status of the full command list following it.
The `case` Statement
Sometimes you want to choose actions based on a single variable that can have many possible values. Instead of writing many elif branches, shell provides the case statement.
Basic structure:
case WORD in
PATTERN1)
COMMANDS1
;;
PATTERN2)
COMMANDS2
;;
*)
DEFAULT_COMMANDS
;;
esac
The WORD is often a variable. Each PATTERN can use shell patterns such as * and ?. The first matching pattern runs its commands. The ;; ends that block.
Example:
read -p "Enter a number from 1 to 3: " NUM
case "$NUM" in
1)
echo "You chose one."
;;
2)
echo "You chose two."
;;
3)
echo "You chose three."
;;
*)
echo "Unknown choice."
;;
esac
The * pattern at the end acts like a default branch. It matches anything not handled by previous patterns.
Patterns can include alternatives using |:
case "$ANSWER" in
yes|y|Y)
echo "You answered yes."
;;
no|n|N)
echo "You answered no."
;;
*)
echo "Please answer yes or no."
;;
esac
A case statement is often clearer than many if...elif blocks when everything depends on the same value.
Arithmetic Conditions with `(( ))`
For integer arithmetic and comparisons, the shell has an arithmetic evaluation syntax that is more natural for numbers.
The form (( expression )) treats its content as an arithmetic expression. It returns exit status 0 if the result of the expression is nonzero and nonzero exit status if the result is zero.
This makes it usable directly in if:
In (( expression )), an expression that evaluates to nonzero counts as “true”.
An expression that evaluates to zero counts as “false”.
Example:
COUNT=5
if (( COUNT > 3 )); then
echo "COUNT is greater than 3."
fi
Arithmetic conditions inside (( )) do not need -gt, -lt, and similar operators. Instead, you can use standard C-style operators like >, <, >=, <=, ==, and !=.
You can also combine arithmetic with logical operators && and || inside the arithmetic expression:
if (( COUNT > 3 && COUNT < 10 )); then
echo "COUNT is between 4 and 9."
fiThis form is limited to integer arithmetic and is most useful when you do many numeric comparisons.
Summary of Conditional Evaluation
Conditionals in shell scripting revolve around one core idea: commands succeed or fail, and that success or failure decides which path the script takes.
To use conditionals effectively, remember:
Use if with any command. The block runs when the command exit status is 0.
Use [ or test to compare strings, numbers, and file properties.
Use else and elif to handle multiple branches of logic.
Use && and || to combine conditions and to sequence commands that depend on each other.
Use case when you branch on a single value that can match different patterns.
Use (( )) for arithmetic comparisons that involve integers.
With these tools, your scripts can respond to real situations instead of following a fixed sequence of commands.