Table of Contents
- Conditionals in Shell Scripting
- Loops in Shell Scripting
- Common Practices
- Best Practices
- Conclusion
- References
Conditionals in Shell Scripting
Conditionals allow scripts to make decisions based on whether a statement is “true” or “false.” They enable logic like: “If a file exists, back it up; otherwise, create it.” or “If the user enters ‘yes’, proceed; else, exit.”
If Statements
The if statement is the most basic conditional construct. It executes a block of code only if a specified condition is met.
Syntax:
if [ condition ]; then
# Code to run if condition is true
fi
[ condition ]: A test expression (note the spaces around[and]—this is critical!). The[symbol is an alias for thetestcommand, which evaluates the condition.then: Marks the start of the code block to execute when the condition is true.fi: Closes theifstatement (reverse ofif).
Example: Check if a File Exists
#!/bin/bash
file="example.txt"
if [ -f "$file" ]; then # -f checks if the file exists and is a regular file
echo "$file exists."
fi
Output:
If example.txt exists: example.txt exists.
If not: No output.
If-Else Statements
Use if-else to execute one block of code when a condition is true and another when it’s false.
Syntax:
if [ condition ]; then
# Code if condition is true
else
# Code if condition is false
fi
Example: Check File Existence and Create if Missing
#!/bin/bash
file="example.txt"
if [ -f "$file" ]; then
echo "$file exists."
else
echo "$file does not exist. Creating it..."
touch "$file" # Create the file
fi
Output (if file missing):
example.txt does not exist. Creating it...
If-Elif-Else Chains
For multiple conditions, use if-elif-else (“elif” = “else if”). The first true condition triggers its block; subsequent conditions are ignored.
Syntax:
if [ condition1 ]; then
# Code if condition1 is true
elif [ condition2 ]; then
# Code if condition2 is true (and condition1 is false)
else
# Code if all conditions are false
fi
Example: Categorize a Number
#!/bin/bash
read -p "Enter a number: " num
if [ "$num" -gt 0 ]; then # -gt = greater than
echo "Positive number."
elif [ "$num" -lt 0 ]; then # -lt = less than
echo "Negative number."
else
echo "Zero."
fi
Output:
If input is 5: Positive number.
If input is -3: Negative number.
If input is 0: Zero.
Case Statements
For checking a variable against multiple fixed values (e.g., menu options), case statements are cleaner than long if-elif chains.
Syntax:
case "$variable" in
pattern1)
# Code if variable matches pattern1
;; # Terminate the pattern block
pattern2)
# Code if variable matches pattern2
;;
*) # Default case (matches any value not covered above)
# Code for default
;;
esac # Closes the case statement (reverse of "case")
Example: Simple Menu System
#!/bin/bash
echo "Choose an option:"
echo "1. Greet"
echo "2. Farewell"
echo "3. Exit"
read -p "Enter option (1-3): " choice
case "$choice" in
1)
echo "Hello!"
;;
2)
echo "Goodbye!"
;;
3)
echo "Exiting..."
exit 0 # Exit the script
;;
*)
echo "Invalid option."
;;
esac
Output (if input is 2): Goodbye!
Loops in Shell Scripting
Loops automate repetitive tasks, such as processing files, iterating over numbers, or repeating commands until a condition is met. Shell scripting supports three primary loop types: for, while, and until.
For Loops
for loops iterate over a list of items (e.g., filenames, numbers, command output) and execute a block of code for each item.
Syntax 1: List Iteration
for item in list; do
# Code to run for each item
done
Example: Iterate Over a List of Names
#!/bin/bash
names=("Alice" "Bob" "Charlie")
for name in "${names[@]}"; do # "${names[@]}" expands to all elements of the array
echo "Hello, $name!"
done
Output:
Hello, Alice!
Hello, Bob!
Hello, Charlie!
Syntax 2: C-Style For Loops (Bash-Specific)
For numerical ranges or complex iteration, use C-style syntax:
for ((initialization; condition; increment)); do
# Code
done
Example: Count from 1 to 5
#!/bin/bash
for ((i=1; i<=5; i++)); do
echo "Count: $i"
done
Output:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
Example: Iterate Over Files
Loop over all .txt files in the current directory:
#!/bin/bash
for file in *.txt; do
echo "Found text file: $file"
done
Output (if a.txt and b.txt exist):
Found text file: a.txt
Found text file: b.txt
While Loops
while loops execute a block of code as long as a condition is true. They’re useful for repeating actions until a dynamic condition changes (e.g., user input, file creation).
Syntax:
while [ condition ]; do
# Code to run while condition is true
done
Example: Read Input Until “quit”
#!/bin/bash
echo "Enter text (type 'quit' to exit):"
while read -r input; do # Read user input into $input
if [ "$input" = "quit" ]; then
break # Exit the loop
fi
echo "You entered: $input"
done
Output:
Enter text (type 'quit' to exit):
hello
You entered: hello
world
You entered: world
quit
Until Loops
until loops are the inverse of while loops: they execute code until a condition becomes true. Use them when you need to repeat an action until a target state is reached.
Syntax:
until [ condition ]; do
# Code to run until condition is true
done
Example: Wait for a File to Exist
#!/bin/bash
target_file="ready.txt"
echo "Waiting for $target_file to be created..."
until [ -f "$target_file" ]; do # Loop until the file exists
sleep 1 # Wait 1 second before checking again
done
echo "$target_file found!"
Output (after ready.txt is created in another terminal):
Waiting for ready.txt to be created...
ready.txt found!
Common Practices
To write effective conditionals and loops, follow these widely adopted practices:
1. Use Double Brackets [[ ]] for Enhanced Conditionals
Bash supports [[ ]] (a shell keyword) as an alternative to [ ] (the test command). [[ ]] offers:
- No need to quote variables (safer for spaces in filenames).
- Support for pattern matching (
[[ "$var" == *txt ]]checks ifvarends with “txt”). - Logical operators like
&&(AND) and||(OR) inside the brackets.
Example:
name="document.txt"
if [[ "$name" == *.txt ]]; then # Pattern matching
echo "Text file detected."
fi
2. Quote Variables to Handle Spaces
Always quote variables (e.g., "$file") to avoid issues with filenames or input containing spaces. Without quotes, file="my file.txt" would split into my and file.txt in [ -f $file ], causing errors.
Bad: if [ -f $file ]; then ... (fails if $file has spaces)
Good: if [ -f "$file" ]; then ...
3. Indent Code for Readability
Indent loop/conditional blocks (e.g., with 2 or 4 spaces) to make nested logic easier to follow:
# Good
for item in "${list[@]}"; do
if [ "$item" -gt 10 ]; then
echo "$item is large"
fi
done
4. Combine Conditions with Logical Operators
Use && (AND) and || (OR) to chain conditions:
if [ -f "$file" ] && [ -r "$file" ]; then ...(File exists and is readable)if [ "$num" -lt 0 ] || [ "$num" -gt 100 ]; then ...(Number is negative or > 100)
Best Practices
To write robust, maintainable shell scripts with conditionals and loops:
1. Avoid Overly Complex Loops
If a loop spans hundreds of lines, refactor logic into functions. For example:
process_file() {
local file="$1"
# Logic to process $file
}
# Loop over files and call the function
for file in *.txt; do
process_file "$file"
done
2. Use set -e for Error Handling
Add set -e at the top of your script to exit immediately if any command fails. This prevents invalid states from propagating:
#!/bin/bash
set -e # Exit on error
# Script will exit if this command fails (e.g., file not found)
cp important.txt backup/
3. Validate Inputs in Loops
If a loop relies on user input or external data, validate values to avoid crashes. For example, check if a number is numeric before arithmetic operations:
read -p "Enter a number: " num
if ! [[ "$num" =~ ^[0-9]+$ ]]; then # Regex check for digits
echo "Error: Not a number."
exit 1
fi
4. Test with shellcheck
Use ShellCheck (a static analysis tool) to catch bugs in conditionals/loops. It flags issues like unquoted variables, missing then, or invalid operators.
5. Limit Loop Scope with local Variables
In loops inside functions, declare variables as local to avoid polluting the global scope:
count_files() {
local dir="$1"
local count=0
for file in "$dir"/*; do
count=$((count + 1)) # Increment counter
done
echo "Total files: $count"
}
Conclusion
Conditionals (if, case) and loops (for, while, until) are the backbone of dynamic shell scripts. They enable decision-making and repetition, turning simple command sequences into powerful automation tools. By mastering these constructs and following best practices like quoting variables, using [[ ]], and validating inputs, you can write scripts that are efficient, readable, and robust.
To deepen your skills, practice building real-world tools: a backup script with conditional checks, a log parser with loops, or a system monitor with until loops. For further learning, refer to the official Bash documentation and tools like ShellCheck.
References
- GNU Bash Manual
- ShellCheck: Static Analysis for Shell Scripts
- Linuxize: Bash Loops Tutorial
- tldp.org: Conditional Constructs
- Robbins, A. (2005). Learning the Bash Shell (3rd ed.). O’Reilly Media.