Day 30/30 Days of Linux Mastery: Mastering ‘for’, ‘do’, ‘while’, and ‘case’ Statements in Shell Scripts
Welcome to Day 30 of our 30 Days of Linux Mastery series! Today, weโre tackling the core of shell scripting: control flow statements. Specifically, weโll dive deep into for
, do
, while
, and case
statements. Mastering these constructs will empower you to write powerful, automated, and efficient shell scripts.
Why Learn Control Flow Statements?
Shell scripts without control flow are simply a list of commands executed sequentially. While useful, they lack the intelligence to adapt to different scenarios, iterate over data, or make decisions based on conditions. Control flow statements provide that intelligence, allowing your scripts to:
- Automate Repetitive Tasks: Process multiple files, users, or servers without manual intervention.
- Implement Conditional Logic: Execute different code blocks based on specific conditions (e.g., checking if a file exists, verifying a user’s input).
- Create Dynamic Scripts: Adapt their behavior based on user input, system status, or external data.
Prerequisites
Before we begin, you should have a basic understanding of:
- Basic Linux commands (
ls
,cd
,echo
,mkdir
, etc.) - Shell scripting basics (creating and executing scripts, using variables)
If you’re new to shell scripting, we recommend revisiting the earlier days of this series.
1. The ‘for’ Loop: Iterating Over Lists and Ranges
The for
loop is your go-to tool for iterating over a list of items. It’s incredibly versatile and widely used in shell scripting.
1.1. Basic ‘for’ Loop Syntax
The general syntax of a for
loop is:
for variable in list
do
# Commands to execute for each item in the list
done
Explanation:
for variable in list
: This line defines the loop.variable
is a variable that will hold each item from thelist
in each iteration. Thelist
can be a space-separated list of values, a command substitution, or a variable containing a list.do
: Marks the beginning of the loop’s code block.# Commands to execute...
: This is where you put the commands you want to execute for each item in the list. You can access the current item using the$variable
syntax.done
: Marks the end of the loop’s code block.
1.2. Examples of ‘for’ Loops
1.2.1. Iterating Over a List of Strings
#!/bin/bash
for fruit in apple banana cherry
do
echo "I like $fruit"
done
Output:
I like apple
I like banana
I like cherry
1.2.2. Iterating Over a List of Files
#!/bin/bash
for file in *.txt
do
echo "Processing file: $file"
cat "$file" # Display the contents of the file
done
This script will iterate over all files ending with .txt
in the current directory and print their names and contents.
1.2.3. Using Command Substitution to Generate the List
#!/bin/bash
for user in $(cut -d':' -f1 /etc/passwd)
do
echo "User: $user"
done
This script uses cut
to extract the usernames from the /etc/passwd
file and then iterates over them.
1.2.4. Iterating Over a Range of Numbers
#!/bin/bash
for i in {1..5}
do
echo "Number: $i"
done
Output:
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
You can also specify a step:
#!/bin/bash
for i in {1..10..2} # From 1 to 10, incrementing by 2
do
echo "Number: $i"
done
Output:
Number: 1
Number: 3
Number: 5
Number: 7
Number: 9
1.3. Practical Example: Batch Renaming Files
Let’s create a script to rename all .txt
files in a directory by adding a timestamp to their names.
#!/bin/bash
timestamp=$(date +%Y%m%d%H%M%S)
for file in *.txt
do
new_name="${file%.txt}_${timestamp}.txt"
mv "$file" "$new_name"
echo "Renamed $file to $new_name"
done
Explanation:
timestamp=$(date +%Y%m%d%H%M%S)
: Gets the current date and time in a specific format.new_name="${file%.txt}_${timestamp}.txt"
: Creates the new filename by removing the.txt
extension, adding the timestamp, and then re-adding the.txt
extension.${file%.txt}
is a parameter expansion that removes the shortest matching suffix (.txt
) from the variable$file
.mv "$file" "$new_name"
: Renames the file.
2. The ‘while’ Loop: Repeating Until a Condition is False
The while
loop executes a block of code repeatedly as long as a specified condition is true. It’s ideal for situations where you need to keep looping until a certain event occurs or a particular state is reached.
2.1. Basic ‘while’ Loop Syntax
The general syntax of a while
loop is:
while condition
do
# Commands to execute as long as the condition is true
done
Explanation:
while condition
: The loop continues as long as thecondition
evaluates to true (exit code 0).do
: Marks the beginning of the loop’s code block.# Commands to execute...
: The commands within the loop are executed repeatedly.done
: Marks the end of the loop’s code block.
2.2. Examples of ‘while’ Loops
2.2.1. Simple Counter
#!/bin/bash
count=1
while [ $count -le 5 ] # -le means "less than or equal to"
do
echo "Count: $count"
count=$((count + 1)) # Increment the counter
done
Output:
Count: 1
Count: 2
Count: 3
Count: 4
Count: 5
2.2.2. Reading Input from a File
#!/bin/bash
while IFS= read -r line
do
echo "Line: $line"
done < input.txt
This script reads each line from the input.txt
file and prints it. IFS= read -r line
is a safe and reliable way to read lines from a file, even if they contain spaces or backslashes.
2.2.3. Waiting for a File to Exist
#!/bin/bash
file_to_wait_for="my_file.txt"
while [ ! -f "$file_to_wait_for" ] # ! -f means "file does not exist"
do
echo "Waiting for $file_to_wait_for to exist..."
sleep 5 # Wait for 5 seconds
done
echo "$file_to_wait_for has been created!"
This script waits until the file my_file.txt
is created before continuing.
2.3. Practical Example: Monitoring System Load
Let's create a script to monitor system load and send an alert if it exceeds a threshold.
#!/bin/bash
load_threshold=2.0
while true # Loop indefinitely
do
load_average=$(uptime | awk '{print $(NF-2)}') # Get the 1-minute load average
load_average=$(echo $load_average | tr -d ',') # Remove the comma
if (( $(echo "$load_average > $load_threshold" | bc -l) )) ; then
echo "Warning: Load average ($load_average) exceeds threshold ($load_threshold)!"
# Add code to send an email or other alert here
fi
sleep 60 # Check every minute
done
Explanation:
load_average=$(uptime | awk '{print $(NF-2)}')
: Gets the 1-minute load average from the output of theuptime
command.awk '{print $(NF-2)}'
extracts the second-to-last field (which is the load average).load_average=$(echo $load_average | tr -d ',')
: Removes the comma from the load average string, if present.if (( $(echo "$load_average > $load_threshold" | bc -l) ))
: Compares the load average to the threshold usingbc
for floating-point arithmetic.sleep 60
: Pauses the script for 60 seconds before checking the load average again.while true
: Creates an infinite loop, constantly monitoring system load. You might want to add a condition to break out of the loop after a certain number of iterations or based on another criteria.
3. The 'until' Loop: Repeating Until a Condition is True
The until
loop is similar to the while
loop, but it executes the block of code as long as the condition is false. It stops when the condition becomes true.
3.1. Basic 'until' Loop Syntax
The general syntax of an until
loop is:
until condition
do
# Commands to execute as long as the condition is false
done
3.2. Example: Waiting for a Process to Finish
#!/bin/bash
process_name="my_long_running_process"
until ! pgrep -x "$process_name" > /dev/null
do
echo "Waiting for $process_name to finish..."
sleep 5
done
echo "$process_name has finished!"
This script waits until a process named "my_long_running_process" is no longer running (as determined by pgrep
) before continuing.
4. The 'case' Statement: Implementing Multi-Way Branching
The case
statement allows you to execute different code blocks based on the value of a variable or expression. It's a cleaner and more readable alternative to using a long series of if/elif/else
statements.
4.1. Basic 'case' Statement Syntax
The general syntax of a case
statement is:
case variable in
pattern1)
# Commands to execute if variable matches pattern1
;;
pattern2)
# Commands to execute if variable matches pattern2
;;
*) # Default case
# Commands to execute if no other pattern matches
;;
esac
Explanation:
case variable in
: Starts thecase
statement, specifying the variable to be evaluated.pattern1)
,pattern2)
, etc.: These are the patterns to match against the value of thevariable
.# Commands to execute...
: The commands to be executed if the corresponding pattern matches.;;
: Terminates the code block for each pattern. This is crucial; without it, execution will "fall through" to the next case.*)
: The default case. This is executed if none of the other patterns match.esac
: Marks the end of thecase
statement (case
spelled backwards).
4.2. Examples of 'case' Statements
4.2.1. Handling Different User Inputs
#!/bin/bash
read -p "Enter a command (start, stop, restart): " command
case "$command" in
start)
echo "Starting the service..."
# Add code to start the service here
;;
stop)
echo "Stopping the service..."
# Add code to stop the service here
;;
restart)
echo "Restarting the service..."
# Add code to restart the service here
;;
*)
echo "Invalid command. Please use start, stop, or restart."
;;
esac
4.2.2. Checking File Type
#!/bin/bash
file="my_file.txt"
case "$(file --mime-type -b "$file")" in
text/plain*)
echo "$file is a text file."
;;
image/*)
echo "$file is an image file."
;;
application/pdf)
echo "$file is a PDF file."
;;
*)
echo "Unknown file type."
;;
esac
This script uses the file
command to determine the MIME type of a file and then executes different code based on the type.
4.3. Using Wildcards in 'case' Statements
You can use wildcards in the patterns of a case
statement to match multiple values.
#!/bin/bash
read -p "Enter a number (1-10): " number
case "$number" in
[1-5])
echo "The number is between 1 and 5."
;;
[6-9]|10) #The | allows you to combine multiple patterns.
echo "The number is between 6 and 10."
;;
*)
echo "Invalid number. Please enter a number between 1 and 10."
;;
esac
4.4. Practical Example: Creating a Simple Menu
Let's create a script that presents a menu to the user and executes different actions based on their choice.
#!/bin/bash
while true
do
echo "Menu:"
echo "1. List files"
echo "2. Create directory"
echo "3. Exit"
read -p "Enter your choice: " choice
case "$choice" in
1)
ls -l
;;
2)
read -p "Enter directory name: " dir_name
mkdir "$dir_name"
echo "Directory '$dir_name' created."
;;
3)
echo "Exiting..."
exit 0
;;
*)
echo "Invalid choice. Please enter 1, 2, or 3."
;;
esac
done
5. Combining Control Flow Statements
The real power of shell scripting comes from combining these control flow statements to create complex logic. You can nest for
loops inside while
loops, use case
statements within for
loops, and so on.
5.1. Example: Processing Files in Multiple Directories
#!/bin/bash
directories=("dir1" "dir2" "dir3")
for dir in "${directories[@]}" # Use "${array[@]}" to iterate over all elements of an array
do
echo "Processing directory: $dir"
cd "$dir"
for file in *.txt
do
echo " Processing file: $file"
# Add code to process the file here
done
cd .. # Go back to the parent directory
done
5.2. Example: Using 'case' to Handle Options in a 'while' Loop
#!/bin/bash
while true
do
read -p "Enter an option (a, b, c, or q to quit): " option
case "$option" in
a)
echo "Option A selected."
# Add code for option A here
;;
b)
echo "Option B selected."
# Add code for option B here
;;
c)
echo "Option C selected."
# Add code for option C here
;;
q)
echo "Quitting..."
exit 0
;;
*)
echo "Invalid option. Please enter a, b, c, or q."
;;
esac
done
6. Best Practices and Common Mistakes
6.1. Always Quote Variables
Always enclose variables in double quotes ("$variable"
) to prevent word splitting and globbing issues. This is especially important when dealing with filenames or paths that contain spaces.
6.2. Use 'set -x' for Debugging
The set -x
command enables shell tracing, which prints each command to the console before it's executed. This is invaluable for debugging your scripts.
#!/bin/bash
set -x # Enable tracing
# Your script code here
To disable tracing, use set +x
.
6.3. Check Exit Codes
Every command returns an exit code (0 for success, non-zero for failure). You can check the exit code using the $?
variable. This allows you to handle errors gracefully.
command
if [ $? -ne 0 ]; then
echo "Error: Command failed."
exit 1
fi
6.4. Be Careful with Infinite Loops
Make sure that your while
and until
loops have a condition that will eventually become false (for while
) or true (for until
) to avoid infinite loops. If you do create an infinite loop intentionally, ensure there's a mechanism (like a break
statement or a signal handler) to exit the loop.
6.5. Use Meaningful Variable Names
Choose descriptive variable names to make your code more readable and understandable.
6.6. Indent Your Code
Proper indentation makes your code easier to read and understand. Use consistent indentation throughout your scripts.
7. Advanced Techniques
7.1. 'break' and 'continue' Statements
- break: Exits the current loop prematurely.
- continue: Skips the current iteration of the loop and proceeds to the next iteration.
#!/bin/bash
for i in {1..10}
do
if [ $i -eq 5 ]; then
continue # Skip iteration when i is 5
fi
if [ $i -eq 8 ]; then
break # Exit the loop when i is 8
fi
echo "Number: $i"
done
echo "Loop finished."
7.2. Nested Loops with 'break' and 'continue'
You can use labeled break
and continue
statements to control which loop is affected in nested loop scenarios (bash 4+).
#!/bin/bash
outer_loop: for i in {1..3}
do
inner_loop: for j in {1..3}
do
if [ $i -eq 2 ] && [ $j -eq 2 ]; then
continue outer_loop # Skip the rest of the outer loop's iteration
fi
echo "i=$i, j=$j"
done
done
8. Conclusion
Today, we've covered the fundamental control flow statements in shell scripting: for
, while
, until
, and case
. Mastering these constructs is essential for writing powerful and automated scripts. Practice these concepts with different examples and scenarios to solidify your understanding. Continue experimenting, and you'll soon be writing sophisticated shell scripts with ease!
Congratulations on completing Day 30 of the 30 Days of Linux Mastery series! We hope you've found this journey valuable and that you're now equipped with the skills to confidently navigate the Linux command line and write effective shell scripts.
9. Further Exploration
- Advanced Bash Scripting Guide: A comprehensive guide to all aspects of bash scripting.
- tldp.org: The Linux Documentation Project, a wealth of information on Linux and shell scripting.
- Online forums and communities: Stack Overflow, Reddit's r/linuxquestions, and other online forums are great places to ask questions and learn from other users.
```