LFCS 123 ๐ง Shell Scripting โ Conditionals
The previous chapter covered special parameters: positional arguments and exit codes. This chapter focuses on conditionals: if, elif, else, case, and the test operators that determine which branch runs. The LFCS exam tests the syntax of the if statement, the difference between [ and [[, and the distinction between numeric comparison and string comparison. The critical trap is the unquoted variable in a test: [ $var = "value" ] fails if $var is empty because the test becomes [ = "value" ], which is a syntax error. The fix is to quote the variable: [ "$var" = "value" ].
Key point: The if statement evaluates the exit code of the command it runs. A zero exit code means true. Any non-zero value means false. The [ command (also called test) is a builtin. The [[ keyword is a bash extension with more features: pattern matching with =~, regex matching with =~, and no word splitting. The (( ... )) form is for arithmetic conditions. The case statement matches patterns, not exit codes.
Why conditionals require careful syntax
The exit code problem. The shell does not have boolean values. The if statement evaluates the exit code of the command. The [ command returns 0 (true) if the condition is satisfied, 1 (false) if not, and 2 (error) if the syntax is wrong. The [[ keyword behaves similarly but has fewer error cases. The (( form returns 0 if the arithmetic result is non-zero, 1 if zero.
The test operator problem. The [ command accepts operators for string comparison (=, !=, -z, -n), numeric comparison (-eq, -ne, -lt, -le, -gt, -ge), and file tests (-f, -d, -e, -r, -w, -x, -s, -L). The = operator is for strings. The -eq operator is for numbers. The [ 10 = 10 ] is a string comparison. The [ 10 -eq 10 ] is a numeric comparison. The two produce the same result for this input, but for [ 010 = 10 ] they differ: string comparison is false, numeric comparison is true.
The quoting problem. The unquoted variable in a test is subject to word splitting. If the variable is empty, the test becomes [ = "value" ], which is a syntax error. The fix is to quote the variable: [ "$var" = "value" ]. The [[ form does not require quoting because it does not perform word splitting, but quoting is still a good habit.
The [[ versus [ problem. The [[ keyword is a bash extension. It supports pattern matching ([[ $name == J* ]]), regex matching ([[ $email =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]]), and logical operators without the escaping required by [. The [ command is POSIX and works in any shell. For portability, use [. For bash-specific features, use [[.
The trade-off. The [ command is portable but has more edge cases. The [[ keyword is safer but bash-only. The (( form is for arithmetic only. The case statement is for pattern matching, which is more readable than a chain of if statements with string comparisons.
a. The if, elif, and else structure
The if statement evaluates a command. If the exit code is zero, the then block runs. If not, the elif block is evaluated. If no condition is true, the else block runs.
#!/bin/bash
if [ "$1" -gt 10 ]; then
echo "Greater than 10"
elif [ "$1" -eq 10 ]; then
echo "Equal to 10"
else
echo "Less than 10"
fi
The then keyword can be on the same line as the condition if it is preceded by a semicolon, or on the next line if the condition is on its own line. The fi keyword closes the if block.
The test command can be [, [[, ((, or any command that returns an exit code. The if grep -q "pattern" file; then form runs grep and uses its exit code. A successful grep returns 0, which is true.
if grep -q "TODO" main.c; then
echo "TODO found"
fi
b. String, numeric, and file test operators
The test operators are divided into three categories: string, numeric, and file.
# String comparison
if [ "$name" = "alice" ]; then
echo "Hello, alice"
fi
if [ -z "$name" ]; then
echo "Name is empty"
fi
if [ -n "$name" ]; then
echo "Name is not empty"
fi
The = operator is for string equality. The != is for inequality. The -z is true if the string is empty. The -n is true if the string is not empty.
# Numeric comparison
if [ "$count" -eq 0 ]; then
echo "Count is zero"
fi
if [ "$count" -lt 10 ]; then
echo "Count is less than 10"
fi
The numeric operators are -eq (equal), -ne (not equal), -lt (less than), -le (less than or equal), -gt (greater than), -ge (greater than or equal). They require integer operands. The shell returns an error if the operands are not numeric.
# File tests
if [ -f "$file" ]; then
echo "File exists"
fi
if [ -d "$dir" ]; then
echo "Directory exists"
fi
if [ -x "$script" ]; then
echo "Script is executable"
fi
The file tests are -e (exists), -f (regular file), -d (directory), -r (readable), -w (writable), -x (executable), -s (non-empty), -L (symbolic link).
c. The [[ keyword, arithmetic, and case
The [[ keyword supports pattern matching and regex matching. It does not perform word splitting, so quoting is optional but still recommended.
# Pattern matching
if [[ "$name" == J* ]]; then
echo "Name starts with J"
fi
# Regex matching
if [[ "$email" =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]]; then
echo "Valid email"
fi
The =~ operator matches the left side against the regular expression on the right side. The match is unanchored unless ^ and $ are used. The matched substring is available in BASH_REMATCH.
The (( form is for arithmetic conditions. It returns 0 if the expression is non-zero, 1 if zero.
if (( count > 10 )); then
echo "Count is greater than 10"
fi
The case statement matches the value against patterns. It is the correct choice when there are many possible values for a single variable.
case "$1" in
start)
echo "Starting"
;;
stop)
echo "Stopping"
;;
restart)
echo "Restarting"
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
;;
esac
The * pattern matches any value and is the fallback. The ;; terminates each branch. The ;& and ;;& variants are bash extensions for fallthrough and continued matching.
Complete Example Session
# ============================================
# PART 1: BASIC IF
# ============================================
cat > check.sh <<'EOF'
#!/bin/bash
if [ "$1" -gt 10 ]; then
echo "Greater than 10"
fi
EOF
chmod +x check.sh
./check.sh 15
# Output: Greater than 10
# ============================================
# PART 2: IF-ELIF-ELSE
# ============================================
cat > classify.sh <<'EOF'
#!/bin/bash
if [ "$1" -gt 10 ]; then
echo "Large"
elif [ "$1" -eq 10 ]; then
echo "Exact"
else
echo "Small"
fi
EOF
chmod +x classify.sh
./classify.sh 5
# Output: Small
# ============================================
# PART 3: STRING TEST
# ============================================
cat > greet.sh <<'EOF'
#!/bin/bash
if [ "$1" = "alice" ]; then
echo "Hello, Alice"
else
echo "Hello, stranger"
fi
EOF
chmod +x greet.sh
./greet.sh alice
# Output: Hello, Alice
# ============================================
# PART 4: EMPTY VARIABLE
# ============================================
cat > empty.sh <<'EOF'
#!/bin/bash
if [ -z "$1" ]; then
echo "No argument"
fi
EOF
chmod +x empty.sh
./empty.sh
# Output: No argument
# ============================================
# PART 5: FILE TEST
# ============================================
cat > filecheck.sh <<'EOF'
#!/bin/bash
if [ -f "$1" ]; then
echo "File exists"
else
echo "File not found"
fi
EOF
chmod +x filecheck.sh
./filecheck.sh /etc/passwd
# Output: File exists
# ============================================
# PART 6: [[ PATTERN
# ============================================
cat > pattern.sh <<'EOF'
#!/bin/bash
if [[ "$1" == J* ]]; then
echo "Starts with J"
fi
EOF
chmod +x pattern.sh
./pattern.sh James
# Output: Starts with J
# ============================================
# PART 7: REGEX
# ============================================
cat > regex.sh <<'EOF'
#!/bin/bash
if [[ "$1" =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]]; then
echo "Valid email"
fi
EOF
chmod +x regex.sh
./regex.sh test@example.com
# Output: Valid email
# ============================================
# PART 8: ARITHMETIC
# ============================================
cat > arith.sh <<'EOF'
#!/bin/bash
if (( $1 > 10 )); then
echo "Greater than 10"
fi
EOF
chmod +x arith.sh
./arith.sh 15
# Output: Greater than 10
# ============================================
# PART 9: CASE
# ============================================
cat > case.sh <<'EOF'
#!/bin/bash
case "$1" in
start) echo "Starting" ;;
stop) echo "Stopping" ;;
*) echo "Usage: $0 {start|stop}" ;;
esac
EOF
chmod +x case.sh
./case.sh start
# Output: Starting
The nine parts trace the conditionals: basic if, elif/else, string test, empty variable, file test, [[ pattern, regex, arithmetic, and case.
Quick Reference
Conditionals
| Syntax | Purpose |
|---|---|
if cmd; then ...; fi | Basic conditional |
if ...; then ...; elif ...; then ...; else ...; fi | Multi-branch |
case $var in pattern) ... ;; esac | Pattern match |
String Tests
| Operator | Meaning |
|---|---|
= | Equal |
!= | Not equal |
-z | Empty |
-n | Non-empty |
Numeric Tests
| Operator | Meaning |
|---|---|
-eq | Equal |
-ne | Not equal |
-lt | Less than |
-le | Less than or equal |
-gt | Greater than |
-ge | Greater than or equal |
File Tests
| Operator | Meaning |
|---|---|
-e | Exists |
-f | Regular file |
-d | Directory |
-r | Readable |
-w | Writable |
-x | Executable |
-s | Non-empty |
-L | Symlink |
[[ Operators
| Operator | Meaning |
|---|---|
== | Pattern match |
=~ | Regex match |
&& | Logical AND |
| ` |
Best Practices
โ Do This:
if [ "$var" = "value" ]; then
<!-- Quote the variable --> # โ
if [[ "$name" =~ ^[A-Z] ]]; then
<!-- Use [[ for regex and patterns --> # โ
case "$1" in
start) ... ;;
*) ... ;;
esac
<!-- Use case for multi-value matching --> # โ
โ Don’t Do This:
if [ $var = "value" ]; then
<!-- Fails if var is empty --> # โ
if [ "$a" > "$b" ]; then
# For numbers
<!-- > is string comparison --> # โ
if (($var == "string")); then
<!-- (( )) is arithmetic only --> # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
unary operator expected | Unquoted empty variable | "$var" |
| String comparison fails | Used -eq for strings | Use = |
| Numeric comparison fails | Used = for numbers | Use -eq |
[[ syntax error | Using in /bin/sh | Use bash or [ |
| Case falls through | Missing ;; | Add ;; |
Real-World Examples
1. Basic If
if [ "$1" = "yes" ]; then echo "Confirmed"; fi
2. If-Else
if [ -f "$file" ]; then echo "Found"; else echo "Missing"; fi
3. Elif
if [ "$n" -gt 10 ]; then echo "Big"; elif [ "$n" -eq 10 ]; then echo "Exact"; else echo "Small"; fi
4. Empty Check
if [ -z "$var" ]; then echo "Empty"; fi
5. File Check
if [ -x /usr/bin/env ]; then echo "Executable"; fi
6. Pattern Match
if [[ "$name" == J* ]]; then echo "Starts with J"; fi
7. Regex Match
if [[ "$ip" =~ ^[0-9]+\.[0-9]+\. ]]; then echo "IP"; fi
8. Arithmetic
if (( count > 0 )); then echo "Positive"; fi
9. Case
case "$1" in start) echo "Start" ;; stop) echo "Stop" ;; esac
10. Combined Test
if [ -f "$file" ] && [ -r "$file" ]; then echo "Readable file"; fi
Visual
if Execution
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ if [ condition ]; then โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Exit code of [ condition ] โ
โ โโโ 0 (true) โ then block โ
โ โโโ non-zero โ elif/else โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ fi โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[ vs [[
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ [ ] (POSIX test) โ
โ โโโ Word splitting applies โ
โ โโโ Requires quoting โ
โ โโโ No regex โ
โ โโโ No pattern matching โ
โโโโโโโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ [[ ]] (bash keyword) โ
โ โโโ No word splitting โ
โ โโโ Regex with =~ โ
โ โโโ Pattern with == โ
โ โโโ Logical && and || โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
| Condition evaluation | Exit code (0 = true) |
| POSIX test | [ |
| Bash test | [[ |
| String equality | = |
| Numeric equality | -eq |
| Empty check | -z |
| Non-empty check | -n |
| File exists | -e |
| Regular file | -f |
| Directory | -d |
| Regex match | =~ |
| Arithmetic | (( )) |
| Pattern match | case |
Key takeaways:
- The
ifstatement evaluates exit codes. A zero exit code means true. A non-zero exit code means false. The[command returns0if the condition is true,1if false. - The
[command requires quoting. An unquoted empty variable becomes a syntax error. The"$var"form is the fix. The[[keyword does not perform word splitting, so quoting is optional but recommended. - String and numeric comparisons use different operators. The
=operator is for strings. The-eqoperator is for numbers. The[ "$a" = "$b" ]and[ "$a" -eq "$b" ]are different tests. - The
[[keyword supports regex and patterns. The=~operator is for regular expressions. The==operator with a wildcard is for pattern matching. The&&and||operators are logical combinations. - The
casestatement is for multi-value matching. It is more readable than a chain ofif/elifstatements with string comparisons. The*pattern is the fallback.
Remember: The conditional is the gate. The test operator is the check. The exit code is the result. The if statement evaluates the exit code. The [ and [[ commands return exit codes. The (( form returns exit codes. The case statement matches patterns, not exit codes. The exam tests the syntax and the difference between the operators.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!