Table of Contents
- What is Bash Scripting?
- Getting Started: Your First Bash Script
- Basic Syntax: Variables, Comments, and I/O
- Control Structures: If-Else and Loops
- Functions: Reusable Code Blocks
- Command Substitution: Capture Output of Commands
- Practical Examples: Real-World Scripts
- Debugging: Fixing Common Issues
- Best Practices for Bash Scripting
- Conclusion
- References
What is Bash Scripting?
Bash scripting is the art of writing text files containing a sequence of Linux commands that the Bash shell can execute. Think of it as a “recipe” for the shell: instead of typing commands one by one, you write them down in a script, and the shell runs them automatically.
Why Bash Scripting?
- Automation: Replace repetitive manual tasks (e.g., daily backups, log rotation).
- Consistency: Ensure tasks are executed the same way every time (no human error).
- Efficiency: Save hours by automating multi-step workflows.
- Accessibility: Bash is preinstalled on all Linux systems—no extra tools needed!
Getting Started: Your First Bash Script
Let’s dive in by writing a simple “Hello World” script. Follow these steps:
Step 1: Create the Script File
Open a terminal and use a text editor like nano or vim to create a new file (e.g., hello_world.sh). The .sh extension is conventional for bash scripts.
nano hello_world.sh
Step 2: Add the Shebang Line
The first line of every bash script should be the shebang (#!), which tells the system which interpreter to use. For bash, this is:
#!/bin/bash
Step 3: Write the Script Logic
Below the shebang, add a command to print “Hello, World!” using echo:
#!/bin/bash
# This is a comment: Print a greeting
echo "Hello, World!"
Step 4: Make the Script Executable
By default, the script file won’t have execute permissions. Fix this with chmod:
chmod +x hello_world.sh
Step 5: Run the Script
Execute the script using ./ (to specify the current directory):
./hello_world.sh
Output:
Hello, World!
Congratulations! You’ve written and run your first bash script.
Basic Syntax: Variables, Comments, and I/O
Let’s break down essential bash scripting syntax.
Comments
Use # to add comments (lines ignored by the shell). Comments explain why the code does something, not just what it does:
#!/bin/bash
# This script greets the user
echo "Welcome!" # Print a welcome message
Variables
Variables store data (text, numbers, etc.). Rules for variables:
- No spaces around
=(e.g.,name="Alice"is good;name = "Alice"is bad). - Use descriptive names (e.g.,
backup_dirinstead ofb). - Access variables with
$var_name(e.g.,echo $name).
Example:
#!/bin/bash
# Define variables
name="Alice"
age=30
# Print variables
echo "Name: $name"
echo "Age: $age"
Output:
Name: Alice
Age: 30
Input/Output (I/O)
-
Output: Use
echoto print text to the terminal:echo "This is output" # Basic output echo "Today is $(date)" # Output with dynamic content (more on this later) -
Input: Use
readto get input from the user. The-pflag adds a prompt:#!/bin/bash # Get user input read -p "Enter your name: " name # Greet the user echo "Hello, $name!"Run it:
./greet.sh Enter your name: Bob Hello, Bob!
Control Structures: If-Else and Loops
Control structures let you make decisions and repeat actions.
If-Else Statements
Use if-else to run code based on conditions.
Syntax:
if [ condition ]; then
# Code to run if condition is true
elif [ another_condition ]; then
# Code to run if first condition is false, second is true
else
# Code to run if all conditions are false
fi # Ends the if statement
Common Conditions:
- Numbers:
-eq(equal),-ne(not equal),-lt(less than),-gt(greater than). - Strings:
=(equal),!=(not equal). - Files:
-f "file.txt"(file exists),-d "dir"(directory exists).
Example: Check if a file exists
#!/bin/bash
filename="example.txt"
if [ -f "$filename" ]; then
echo "$filename exists!"
else
echo "$filename does NOT exist."
fi
Loops
Loops repeat code until a condition is met.
For Loop
Iterate over a list of items (e.g., numbers, filenames).
Syntax:
for item in list; do
# Code to run for each item
done
Example: Print numbers 1 to 5
#!/bin/bash
for num in {1..5}; do
echo "Number: $num"
done
Output:
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
While Loop
Run code while a condition is true.
Syntax:
while [ condition ]; do
# Code to run
done
Example: Countdown from 5
#!/bin/bash
count=5
while [ $count -gt 0 ]; do
echo "Countdown: $count"
count=$((count - 1)) # Decrement count
sleep 1 # Wait 1 second
done
echo "Blast off!"
Functions: Reusable Code Blocks
Functions let you group code into reusable blocks. Use them to avoid repeating code.
Syntax:
function_name() {
# Code here
# Access arguments with $1, $2, etc. (first, second argument)
}
Example: A greeting function
#!/bin/bash
# Define a function
greet() {
local name=$1 # Local variable (only exists in the function)
echo "Hello, $name! Welcome."
}
# Call the function with an argument
greet "Charlie"
Output:
Hello, Charlie! Welcome.
Command Substitution: Capture Output of Commands
Use command substitution to store the output of a command in a variable. Use $(command) (preferred) or `command` (legacy syntax).
Example: Get the current date
#!/bin/bash
current_date=$(date +"%Y-%m-%d") # Format date as YYYY-MM-DD
echo "Today is $current_date"
Output:
Today is 2024-05-20
Another Example: Count files in a directory
file_count=$(ls | wc -l)
echo "There are $file_count files in this directory."
Practical Examples: Real-World Scripts
Let’s build scripts that solve actual problems.
Example 1: Automated Backup Script
This script backs up a directory to a .tar.gz file and logs the action.
#!/bin/bash
# Backup Script: Backs up a source directory to a target location
# Configuration
source_dir="/home/user/documents" # Directory to back up
target_dir="/mnt/backup" # Where to store the backup
backup_name="docs_backup_$(date +%Y%m%d_%H%M%S).tar.gz" # Unique filename with timestamp
# Check if source directory exists
if [ ! -d "$source_dir" ]; then
echo "Error: Source directory $source_dir does not exist."
exit 1 # Exit with error code 1
fi
# Create backup using tar
echo "Creating backup: $target_dir/$backup_name"
tar -czf "$target_dir/$backup_name" "$source_dir"
# Check if backup succeeded
if [ $? -eq 0 ]; then # $? is the exit code of the last command (0 = success)
echo "Backup completed successfully!"
# Log the backup to a file
echo "$(date): Backup created: $target_dir/$backup_name" >> "$target_dir/backup_log.txt"
else
echo "Error: Backup failed."
exit 1
fi
Example 2: System Cleanup Script
Free up disk space by deleting temporary files and clearing caches.
#!/bin/bash
# System Cleanup Script: Removes temp files and clears caches
echo "Starting system cleanup..."
# Remove temporary files (use with caution!)
echo "Clearing /tmp directory..."
sudo rm -rf /tmp/* # Deletes all files in /tmp (safe for most systems)
# Clear apt cache (Debian/Ubuntu)
echo "Clearing apt cache..."
sudo apt clean
# Clear user cache (e.g., browser cache)
echo "Clearing user cache..."
rm -rf ~/.cache/*
echo "Cleanup complete!"
Example 3: User Creation Script
Create a new system user with a home directory (useful for admins).
#!/bin/bash
# User Creation Script: Adds a new user with a home directory
read -p "Enter username: " username
# Check if user already exists
if id "$username" &>/dev/null; then # Suppress output with &>/dev/null
echo "Error: User $username already exists."
exit 1
fi
# Create user with home directory and bash shell
sudo useradd -m -s /bin/bash "$username" # -m = create home dir; -s = set shell
# Check if useradd succeeded
if [ $? -eq 0 ]; then
echo "User $username created successfully!"
echo "Setting password for $username..."
sudo passwd "$username" # Prompt to set password
else
echo "Error: Failed to create user $username."
exit 1
fi
Debugging: Fixing Common Issues
Even pros make mistakes! Here’s how to debug scripts:
Enable Debugging Mode
Add set -x at the top of your script to print each command before it runs (great for tracking execution flow):
#!/bin/bash
set -x # Enable debugging
echo "Hello"
name="Debug"
echo "Name: $name"
Output will show commands with + prefixes:
+ echo Hello
Hello
+ name=Debug
+ echo Name: Debug
Name: Debug
Check Exit Codes
The $? variable holds the exit code of the last command (0 = success, non-zero = error). Use it to debug failures:
ls non_existent_file
echo "Exit code: $?" # Output: Exit code: 2 (error)
Use shellcheck
shellcheck is a tool that flags syntax errors and bad practices. Install it with sudo apt install shellcheck (Debian/Ubuntu) or sudo dnf install shellcheck (Fedora).
Run it on your script:
shellcheck my_script.sh
Best Practices for Bash Scripting
Write clean, maintainable scripts with these tips:
- Use Descriptive Names: Name scripts like
backup_docs.sh, notscript1.sh. - Add Comments: Explain why (not just what) the code does.
- Handle Errors: Use
set -eto exit the script if any command fails:#!/bin/bash set -e # Exit on error - Quote Variables: Always quote variables (e.g.,
"$filename") to handle spaces in filenames:bad: rm $file # Fails if $file has spaces good: rm "$file" # Works with spaces - Test Thoroughly: Run scripts with test data before deploying to production.
- Reuse Code with Functions: Avoid repeating code—use functions!
Conclusion
Bash scripting is a powerful tool for automating Linux tasks, saving time, and reducing errors. With the basics covered—variables, control structures, functions, and command substitution—you can now write scripts to handle backups, system maintenance, user management, and more.
The key to mastery is practice: start small (e.g., a script to organize downloads), then tackle bigger projects. As you learn, explore advanced topics like arrays, regular expressions, and integrating tools like awk or sed for text processing.
Happy scripting!