Shell Scripting Fundamentals: Your Complete Guide to Bash Automation
Shell scripting is the backbone of automation in Unix-like systems. Whether you’re a system administrator, DevOps engineer, or developer, mastering shell scripting will significantly boost your productivity and automation capabilities.
Table of Contents
- Getting Started
- Variables and Data Types
- Control Structures
- Functions
- File Operations
- Error Handling
- Best Practices
- Real-World Examples
- Additional Resources
Getting Started
Your First Script
Every shell script starts with a shebang (#!
) that tells the system which interpreter to use:
#!/bin/bash
# This is your first shell script
echo "Hello, World!"
echo "Today's date is: $(date)"
Save this as hello.sh
, make it executable, and run it:
chmod +x hello.sh
./hello.sh
Making Scripts Portable
For maximum compatibility, use the env command:
#!/usr/bin/env bash
# This finds bash wherever it's installed
Variables and Data Types
Basic Variables
#!/bin/bash
# String variables
name="John Doe"
project="Shell Scripting Tutorial"
# Numeric variables
count=42
version=1.5
# Using variables
echo "Author: $name"
echo "Project: ${project}"
echo "Count: $count"
Environment Variables
#!/bin/bash
# Reading environment variables
echo "Current user: $USER"
echo "Home directory: $HOME"
echo "Current path: $PWD"
# Setting environment variables
export MY_VAR="Hello World"
Command Substitution
#!/bin/bash
# Old style (backticks)
current_date=`date`
# New style (preferred)
current_date=$(date)
files_count=$(ls -1 | wc -l)
echo "Current date: $current_date"
echo "Files in directory: $files_count"
Control Structures
Conditional Statements
#!/bin/bash
# Basic if-else
if [ "$1" = "start" ]; then
echo "Starting service..."
elif [ "$1" = "stop" ]; then
echo "Stopping service..."
else
echo "Usage: $0 {start|stop}"
exit 1
fi
# File testing
file="$1"
if [ -f "$file" ]; then
echo "$file exists and is a regular file"
elif [ -d "$file" ]; then
echo "$file exists and is a directory"
else
echo "$file does not exist"
fi
Loops
#!/bin/bash
# For loop with range
echo "Counting from 1 to 5:"
for i in {1..5}; do
echo "Number: $i"
done
# For loop with array
fruits=("apple" "banana" "orange")
for fruit in "${fruits[@]}"; do
echo "I like $fruit"
done
# While loop
counter=1
while [ $counter -le 3 ]; do
echo "Iteration: $counter"
((counter++))
done
# Reading file line by line
while IFS= read -r line; do
echo "Line: $line"
done < "input.txt"
Case Statements
#!/bin/bash
action="$1"
case $action in
start)
echo "Starting application..."
;;
stop)
echo "Stopping application..."
;;
restart)
echo "Restarting application..."
;;
status)
echo "Checking status..."
;;
*)
echo "Unknown action: $action"
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
Functions
Basic Functions
#!/bin/bash
# Function definition
greet() {
local name="$1"
local time="$2"
echo "Good $time, $name!"
}
# Function with return value
is_file_exist() {
local file="$1"
if [ -f "$file" ]; then
return 0 # success
else
return 1 # failure
fi
}
# Using functions
greet "Alice" "morning"
if is_file_exist "/etc/passwd"; then
echo "Password file exists"
fi
Advanced Function Example
#!/bin/bash
# Backup function with error handling
backup_directory() {
local source_dir="$1"
local backup_dir="$2"
local timestamp=$(date +"%Y%m%d_%H%M%S")
# Validate parameters
if [ $# -ne 2 ]; then
echo "Usage: backup_directory <source> <destination>"
return 1
fi
if [ ! -d "$source_dir" ]; then
echo "Error: Source directory '$source_dir' does not exist"
return 1
fi
# Create backup
local backup_name="backup_${timestamp}.tar.gz"
local full_backup_path="${backup_dir}/${backup_name}"
echo "Creating backup: $full_backup_path"
tar -czf "$full_backup_path" -C "$(dirname "$source_dir")" "$(basename "$source_dir")"
if [ $? -eq 0 ]; then
echo "Backup completed successfully: $full_backup_path"
return 0
else
echo "Backup failed"
return 1
fi
}
File Operations
Reading and Writing Files
#!/bin/bash
# Writing to files
echo "This is line 1" > output.txt
echo "This is line 2" >> output.txt
# Reading files
content=$(cat output.txt)
echo "File content: $content"
# Processing CSV files
process_csv() {
local csv_file="$1"
local line_number=0
while IFS=',' read -r col1 col2 col3; do
((line_number++))
if [ $line_number -eq 1 ]; then
echo "Headers: $col1 | $col2 | $col3"
else
echo "Row $((line_number-1)): $col1 | $col2 | $col3"
fi
done < "$csv_file"
}
File Manipulation
#!/bin/bash
# File information
get_file_info() {
local file="$1"
if [ -f "$file" ]; then
echo "File: $file"
echo "Size: $(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null) bytes"
echo "Modified: $(stat -f%Sm "$file" 2>/dev/null || stat -c%y "$file" 2>/dev/null)"
echo "Permissions: $(stat -f%A "$file" 2>/dev/null || stat -c%a "$file" 2>/dev/null)"
else
echo "File '$file' not found"
fi
}
# Directory operations
organize_files() {
local source_dir="$1"
for file in "$source_dir"/*; do
if [ -f "$file" ]; then
extension="${file##*.}"
extension_dir="$source_dir/$extension"
mkdir -p "$extension_dir"
mv "$file" "$extension_dir/"
echo "Moved $(basename "$file") to $extension_dir/"
fi
done
}
Error Handling
Basic Error Handling
#!/bin/bash
# Exit on any error
set -e
# Exit on undefined variable
set -u
# Show commands being executed (for debugging)
# set -x
# Error handling function
handle_error() {
local exit_code=$?
local line_number=$1
echo "Error occurred in script at line $line_number: exit code $exit_code"
exit $exit_code
}
# Trap errors
trap 'handle_error $LINENO' ERR
Advanced Error Handling
#!/bin/bash
# Logging function
log() {
local level="$1"
shift
local message="$*"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] [$level] $message" | tee -a script.log
}
# Safe command execution
safe_execute() {
local command="$*"
log "INFO" "Executing: $command"
if eval "$command"; then
log "SUCCESS" "Command completed successfully"
return 0
else
local exit_code=$?
log "ERROR" "Command failed with exit code: $exit_code"
return $exit_code
fi
}
# Usage example
safe_execute "ls -la /nonexistent" || {
log "WARNING" "Directory listing failed, continuing..."
}
Best Practices
Script Template
#!/usr/bin/env bash
# Script: template.sh
# Description: Template for shell scripts
# Author: Your Name
# Date: $(date)
# Version: 1.0
set -euo pipefail # Exit on error, undefined vars, pipe failures
# Global variables
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly SCRIPT_NAME="$(basename "$0")"
readonly LOG_FILE="${SCRIPT_DIR}/${SCRIPT_NAME%.sh}.log"
# Colors for output
readonly RED='\033[0;31m'
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly NC='\033[0m' # No Color
# Logging functions
log_info() {
echo -e "${GREEN}[INFO]${NC} $*" | tee -a "$LOG_FILE"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $*" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $*" | tee -a "$LOG_FILE"
}
# Cleanup function
cleanup() {
log_info "Cleaning up..."
# Add cleanup code here
}
# Trap cleanup function
trap cleanup EXIT
# Usage function
usage() {
cat << EOF
Usage: $SCRIPT_NAME [OPTIONS]
OPTIONS:
-h, --help Show this help message
-v, --verbose Enable verbose output
-d, --debug Enable debug mode
EXAMPLES:
$SCRIPT_NAME --verbose
$SCRIPT_NAME --debug
EOF
}
# Main function
main() {
local verbose=false
local debug=false
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h|--help)
usage
exit 0
;;
-v|--verbose)
verbose=true
shift
;;
-d|--debug)
debug=true
set -x
shift
;;
*)
log_error "Unknown option: $1"
usage
exit 1
;;
esac
done
log_info "Script started"
# Your main logic here
log_info "Script completed successfully"
}
# Run main function with all arguments
main "$@"
Real-World Examples
System Health Check Script
#!/bin/bash
# System health monitoring script
system_health_check() {
echo "=== System Health Check Report ==="
echo "Date: $(date)"
echo "Hostname: $(hostname)"
echo
# CPU Usage
echo "=== CPU Usage ==="
top -bn1 | grep "Cpu(s)" | sed "s/.*, *\([0-9.]*\)%* id.*/\1/" | awk '{print "CPU Usage: " 100-$1 "%"}'
echo
# Memory Usage
echo "=== Memory Usage ==="
free -h | awk '/^Mem:/ {printf "Memory Usage: %s/%s (%.2f%%)\n", $3,$2,$3/$2*100}'
echo
# Disk Usage
echo "=== Disk Usage ==="
df -h | awk '$NF=="/"{printf "Root Disk Usage: %s/%s (%s)\n", $3,$2,$5}'
echo
# Load Average
echo "=== Load Average ==="
uptime | awk -F'load average:' '{print "Load Average:" $2}'
echo
# Network Connectivity
echo "=== Network Check ==="
if ping -c 1 google.com &> /dev/null; then
echo "Internet: Connected"
else
echo "Internet: Disconnected"
fi
echo
# Service Status
echo "=== Critical Services ==="
for service in ssh nginx mysql; do
if systemctl is-active --quiet $service 2>/dev/null; then
echo "$service: Running"
else
echo "$service: Not running"
fi
done
}
system_health_check
Automated Backup Script
#!/bin/bash
# Automated backup script with rotation
BACKUP_SOURCE="/home/user/important_data"
BACKUP_DEST="/backup"
RETENTION_DAYS=7
LOG_FILE="/var/log/backup.log"
log_message() {
echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}
create_backup() {
local timestamp=$(date +"%Y%m%d_%H%M%S")
local backup_name="backup_${timestamp}.tar.gz"
local backup_path="${BACKUP_DEST}/${backup_name}"
log_message "Starting backup of $BACKUP_SOURCE"
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DEST"
# Create compressed backup
if tar -czf "$backup_path" -C "$(dirname "$BACKUP_SOURCE")" "$(basename "$BACKUP_SOURCE")"; then
log_message "Backup created successfully: $backup_path"
# Calculate backup size
local size=$(du -h "$backup_path" | cut -f1)
log_message "Backup size: $size"
return 0
else
log_message "ERROR: Backup creation failed"
return 1
fi
}
cleanup_old_backups() {
log_message "Cleaning up backups older than $RETENTION_DAYS days"
find "$BACKUP_DEST" -name "backup_*.tar.gz" -type f -mtime +$RETENTION_DAYS -delete
local remaining=$(find "$BACKUP_DEST" -name "backup_*.tar.gz" -type f | wc -l)
log_message "Remaining backups: $remaining"
}
# Main execution
if create_backup; then
cleanup_old_backups
log_message "Backup process completed successfully"
else
log_message "ERROR: Backup process failed"
exit 1
fi
Additional Resources
Whether you’re just starting with shell scripting or looking to advance your skills, these resources will help you on your journey:
๐ฎ Online Playgrounds & Sandboxes
Practice shell scripting directly in your browser without installing anything:
Browser-Based Terminals
- JSLinux - Full Linux system running in browser with multiple distros available
- Copy.sh - v86 - x86 virtualization in JavaScript with various OS options including Linux
- WebMinal - Free online Linux terminal for learning and practicing
- Tutorialspoint Online Terminal - Simple browser-based Linux terminal
Cloud IDEs with Terminal Access
- Repl.it (Replit) - Full-featured online IDE with Bash support and file system
- CodePen - While primarily for web development, supports basic shell commands
- GitPod - Cloud development environment with full Linux terminal (free tier available)
- GitHub Codespaces - VS Code in the browser with terminal access (free tier)
- StackBlitz - Instant dev environments with terminal support
Interactive Learning Platforms
- Katacoda - Interactive Linux and shell scripting scenarios
- Play with Docker - Free Docker playground that includes Linux terminals
- Killercoda - Interactive learning platform with hands-on Linux labs
- Instruqt - Interactive learning tracks with virtual environments
Shell-Specific Playgrounds
- Bash Online - Simple online Bash script executor
- OneCompiler - Bash - Online Bash compiler and runner with syntax highlighting
- JDoodle - Bash - Execute Bash scripts online with input/output support
- Paiza.io - Online coding environment supporting multiple languages including Bash
Virtual Lab Environments
- VirtualBox + Vagrant - Create local virtual machines for practice (free but requires installation)
- Docker Playground - Free online Docker environment with Linux containers
- Linux Containers (LXC) - Try LXC containers online
Mobile-Friendly Options
- iSH Shell (iOS) - Linux shell environment on iOS devices
- Termux (Android) - Full Linux terminal emulator for Android
- UserLAnd (Android) - Run Linux distributions on Android
Quick Script Testing
- ShellCheck Online - Not a playground but essential for validating shell scripts
- Explain Shell - Understand what shell commands do before running them
- Regex101 - Test regular expressions used in shell scripts
Getting Started Tips for Playgrounds:
- Start Simple: Begin with basic commands like
ls
,pwd
,echo
- Create Test Files: Use
touch
andecho
to create files for practice - Practice File Operations: Try moving, copying, and editing files
- Test Your Scripts: Copy-paste the examples from this blog and run them
- Experiment Safely: These environments are isolated, so feel free to experiment
Recommended Workflow:
- Read the tutorial section in this blog
- Try examples in an online playground like Replit or JSLinux
- Modify and experiment with the code
- Create your own scripts based on what you learned
- Test edge cases and error handling
- Share your creations with the community
Remember: Online playgrounds are perfect for learning and quick testing, but for serious development work, consider setting up a local Linux environment or using a more robust cloud IDE.
๐ Beginner Tutorials
- TutorialsPoint - Shell Scripting - Comprehensive tutorial covering all shell scripting basics with examples
- Linux Shell Scripting Tutorial - Step-by-step guide from basic to advanced concepts
- Bash Academy - Interactive guide to learn Bash scripting fundamentals
- Learn Shell - Interactive shell scripting tutorial with hands-on exercises
๐ Interactive Learning Platforms
- Codecademy - Learn the Command Line - Interactive course covering command line and bash basics
- Linux Journey - Command Line - Free, beginner-friendly Linux and shell tutorial
- OverTheWire - Bandit - Gamified way to learn Linux commands and shell scripting
- HackerRank - Linux Shell - Practice shell scripting with coding challenges
๐ Advanced Resources
- Advanced Bash-Scripting Guide (ABS) - Comprehensive guide to advanced Bash scripting techniques
- Bash Hackers Wiki - In-depth documentation and advanced Bash concepts
- Google Shell Style Guide - Best practices for writing maintainable shell scripts
- ShellCheck - Online tool to check shell scripts for common errors and improvements
๐ง Reference & Documentation
- Bash Manual - Official GNU Bash reference manual
- POSIX Shell Standard - Standard for portable shell scripting
- Explain Shell - Break down and explain any shell command
- Cheat.sh - Quick reference and cheat sheets for Bash commands
๐ ๏ธ Tools & Utilities
- Shellharden - Tool to help make your shell scripts more robust
- Bats - Bash Automated Testing System for testing shell scripts
- Shfmt - Shell script formatter and parser
- Fish Shell - User-friendly command line shell with great scripting capabilities
๐บ Video Tutorials
- The Linux Command Line Full Course - Comprehensive video tutorial on YouTube
- Bash Scripting on Linux - Complete playlist covering Bash scripting concepts
- Learn Linux TV - Bash Scripting - Regular updates on Linux and Bash scripting tutorials
๐ Practice Platforms
- LeetCode - Shell - Shell scripting problems to practice
- Exercism - Bash Track - Coding exercises with mentoring for Bash
- Codewars - Shell - Community-driven coding challenges
๐ฑ Mobile Apps
- Termux (Android) - Terminal emulator with Linux environment
- iSH Shell (iOS) - Linux shell for iOS devices
- Learn Unix Commands - Mobile app for learning Unix commands
๐ Communities & Forums
- r/bash - Reddit community for Bash discussions
- Unix & Linux Stack Exchange - Q&A platform for Unix and Linux questions
- Shell Scripting Discord - Real-time discussions with shell scripting enthusiasts
- #bash IRC Channel - IRC channel for Bash help and discussions
๐ Books (Free Online)
- The Linux Command Line by William Shotts - Complete book available free online
- Bash Guide for Beginners - Comprehensive beginner’s guide
- Introduction to Linux - Complete Linux introduction including shell scripting
๐ฏ Specialized Topics
- AWK Tutorial - Learn AWK for text processing
- SED Tutorial - Master the stream editor
- Regular Expressions - Interactive regex learning and testing
- Cron Jobs Tutorial - Learn to schedule scripts with cron
Remember: The best way to learn shell scripting is by practicing! Start with simple scripts and gradually work your way up to more complex automation tasks.
Conclusion
Shell scripting is a powerful tool for automation and system administration. Start with simple scripts and gradually incorporate more advanced features as you become comfortable with the basics.
Key Takeaways:
- Always use proper error handling - Don’t let your scripts fail silently
- Make your scripts readable - Use meaningful variable names and comments
- Test thoroughly - Test your scripts in different environments
- Follow conventions - Use consistent coding style and structure
- Log everything - Keep track of what your scripts are doing
Next Steps:
- Practice with the examples provided
- Create your own automation scripts for daily tasks
- Learn about advanced topics like signal handling and process management
- Explore tools like
shellcheck
for script validation
Happy scripting! ๐
Found this helpful? Share your own shell scripting tips and tricks in the comments below!