Table of Contents
- Why Bash Automation Matters
- Bash Basics You Need to Know
- Practical Automation Examples
- Advanced Bash Tips for Power Users
- Best Practices for Maintainable Scripts
- Tools to Supercharge Your Bash Scripts
- Conclusion
- References
Why Bash Automation Matters
Before we write our first script, let’s clarify why bash automation is worth your time:
- Time Savings: Automate repetitive tasks (e.g., backups, log cleanup) and reclaim hours weekly.
- Consistency: Scripts execute tasks the same way every time, reducing human error.
- Accessibility: Bash is pre-installed on Linux/macOS, no extra tools required.
- Integration: Bash plays well with Linux’s ecosystem (e.g.,
cronfor scheduling,awk/sedfor text processing). - Skill Building: Learning bash sharpens your command-line fluency—a critical skill for Linux users.
Real-World Scenario: Imagine manually zipping your project folder, moving it to an external drive, and deleting old backups every night. A 5-line bash script + cron can do this automatically while you sleep.
Bash Basics You Need to Know
You don’t need to be a programmer to write bash scripts, but a few fundamentals will go a long way. Let’s cover the essentials:
1. Shebang Line
Every script starts with #!/bin/bash (or #!/bin/sh for POSIX compliance). This tells the system to use bash to run the script.
2. Variables
Store data in variables for reuse:
name="Linux User"
echo "Hello, $name!" # Output: Hello, Linux User!
- Use
$VARto access variables;{VAR}for clarity (e.g.,${name}_files). - Special variables:
$0: Script name.$1, $2...: Command-line arguments (e.g.,./script.sh arg1 arg2→$1=arg1).$#: Number of arguments.$?: Exit code of the last command (0 = success, non-zero = error).
3. Loops
Repeat actions with for, while, or until loops:
# For loop: Iterate over files in a directory
for file in ~/Downloads/*.txt; do
echo "Processing $file"
done
# While loop: Run until condition fails
count=1
while [ $count -le 5 ]; do
echo "Count: $count"
count=$((count + 1)) # Increment count
done
4. Conditionals
Execute code based on conditions with if-else or case statements:
# Check if a file exists
file="data.txt"
if [ -f "$file" ]; then
echo "$file exists!"
else
echo "$file not found."
fi
# Case statement for multiple conditions
day=$(date +%A)
case $day in
Monday) echo "Start of the workweek!" ;;
Friday) echo "Weekend is near!" ;;
*) echo "Just another day." ;;
esac
5. Executing Commands
Run system commands directly in scripts. Use $() to capture output:
current_time=$(date "+%Y-%m-%d %H:%M:%S")
echo "Script ran at: $current_time"
Practical Automation Examples
Let’s build scripts for common Linux tasks. Each example includes a problem statement, script code, explanation, and usage instructions.
Example 1: Daily File Backup Script
Problem: You want to back up a project folder to an external drive and keep only the last 7 backups.
Script Code (backup_project.sh):
#!/bin/bash
# Configuration
SOURCE_DIR="$HOME/projects/my_app" # Folder to back up
DEST_DIR="/mnt/external_drive/backups" # Where to save backups
BACKUP_NAME="my_app_backup_$(date +%Y%m%d).tar.gz" # Name with timestamp
# Create backup
echo "Creating backup: $BACKUP_NAME..."
tar -czf "$DEST_DIR/$BACKUP_NAME" -C "$SOURCE_DIR" .
# Check if backup succeeded
if [ $? -eq 0 ]; then
echo "Backup completed!"
else
echo "ERROR: Backup failed!"
exit 1
fi
# Delete backups older than 7 days
echo "Cleaning up old backups..."
find "$DEST_DIR" -name "my_app_backup_*.tar.gz" -mtime +7 -delete
echo "Done!"
Explanation:
tar -czf: Creates a compressed (z) archive (c) with verbose output (v) and saves to a file (f).-C "$SOURCE_DIR" .: Changes toSOURCE_DIRbefore archiving to avoid including the full path.$? -eq 0: Checks if the last command (tar) succeeded (exit code 0 = success).find ... -mtime +7 -delete: Deletes files older than 7 days.
How to Use:
- Save the script as
backup_project.sh. - Make it executable:
chmod +x backup_project.sh. - Test it:
./backup_project.sh. - Schedule daily runs with
cron:- Run
crontab -e. - Add:
0 2 * * * /home/your_user/backup_project.sh(runs at 2 AM daily).
- Run
Example 2: Log Rotation & Cleanup
Problem: Your app’s log file (app.log) grows indefinitely, wasting disk space.
Script Code (rotate_logs.sh):
#!/bin/bash
LOG_FILE="/var/log/my_app/app.log"
MAX_SIZE=10M # Rotate when log reaches 10MB
BACKUP_DIR="/var/log/my_app/old_logs"
# Create backup dir if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Check log size
if [ $(du -b "$LOG_FILE" | cut -f1) -ge $(numfmt --from=iec "$MAX_SIZE") ]; then
echo "Log file too large. Rotating..."
# Rename current log to app.log.YYYYMMDD
mv "$LOG_FILE" "$BACKUP_DIR/app.log_$(date +%Y%m%d)"
# Compress the rotated log
gzip "$BACKUP_DIR/app.log_$(date +%Y%m%d)"
# Create a new empty log file
touch "$LOG_FILE"
# Set permissions (match original log)
chmod --reference="$BACKUP_DIR/app.log_$(date +%Y%m%d).gz" "$LOG_FILE"
echo "Log rotated successfully."
else
echo "Log size is under $MAX_SIZE. No rotation needed."
fi
How to Use:
- Run manually:
./rotate_logs.sh. - For hourly checks, add to
cron:0 * * * * /path/to/rotate_logs.sh.
Example 3: System Cleanup Utility
Problem: Free up disk space by deleting temp files, clearing apt cache, and removing unused packages.
Script Code (system_cleanup.sh):
#!/bin/bash
echo "=== Starting System Cleanup ==="
# Delete user temp files (older than 7 days)
echo "Cleaning user temp files..."
rm -rf ~/.cache/* ~/.local/share/Trash/files/* 2>/dev/null
# Clear apt cache (requires sudo)
echo "Clearing apt cache..."
sudo apt clean
# Remove unused packages (autoremove)
echo "Removing unused packages..."
sudo apt autoremove -y
# Clean system temp files (older than 7 days)
echo "Cleaning system temp files..."
sudo find /tmp -type f -mtime +7 -delete
echo "=== Cleanup Complete ==="
Note: Test with echo first (e.g., echo rm -rf ~/.cache/*) to avoid accidental deletions!
Example 4: Automated File Organizer
Problem: Your Downloads folder is a mess—images, PDFs, and zip files are all mixed.
Script Code (organize_downloads.sh):
#!/bin/bash
DOWNLOADS_DIR="$HOME/Downloads"
# Create folders if missing
mkdir -p "$DOWNLOADS_DIR/{Images,PDFs,Zips,Docs,Other}"
# Move files by extension
echo "Organizing Downloads..."
# Images (jpg, png, jpeg, gif)
find "$DOWNLOADS_DIR" -maxdepth 1 -type f \( -iname "*.jpg" -o -iname "*.png" -o -iname "*.jpeg" -o -iname "*.gif" \) -exec mv {} "$DOWNLOADS_DIR/Images/" \;
# PDFs
find "$DOWNLOADS_DIR" -maxdepth 1 -type f -iname "*.pdf" -exec mv {} "$DOWNLOADS_DIR/PDFs/" \;
# Zips/Archives
find "$DOWNLOADS_DIR" -maxdepth 1 -type f \( -iname "*.zip" -o -iname "*.tar.gz" -o -iname "*.7z" \) -exec mv {} "$DOWNLOADS_DIR/Zips/" \;
# Documents (docx, doc, txt, md)
find "$DOWNLOADS_DIR" -maxdepth 1 -type f \( -iname "*.docx" -o -iname "*.doc" -o -iname "*.txt" -o -iname "*.md" \) -exec mv {} "$DOWNLOADS_DIR/Docs/" \;
# Everything else
find "$DOWNLOADS_DIR" -maxdepth 1 -type f ! -iname "organize_downloads.sh" -exec mv {} "$DOWNLOADS_DIR/Other/" \;
echo "Downloads organized!"
Pro Tip: Run this on login by adding it to your .bashrc or .profile:
# Add to ~/.bashrc
if [ -f "$HOME/organize_downloads.sh" ]; then
"$HOME/organize_downloads.sh"
fi
Example 5: Custom System Monitoring
Problem: You want a quick overview of CPU, memory, and disk usage.
Script Code (system_monitor.sh):
#!/bin/bash
echo "=== System Monitor ==="
echo "CPU Usage: $(top -bn1 | grep "Cpu(s)" | awk '{print $2 + $4}')%"
echo "Memory Usage: $(free -h | awk '/Mem:/ {print $3 "/" $2 " (" $3/$2*100 "%)"}')"
echo "Disk Usage: $(df -h / | awk '/\// {print $3 "/" $2 " (" $5 ")"}')"
echo "=== End ==="
Output:
=== System Monitor ===
CPU Usage: 12.5%
Memory Usage: 2.3G/7.7G (29.87%)
Disk Usage: 45G/237G (19%)
=== End ===
Advanced Bash Tips for Power Users
Once you’re comfortable with basics, these tips will level up your scripts:
1. Use Functions to Avoid Repetition
Group reusable code into functions:
#!/bin/bash
# Function to log messages
log() {
echo "[$(date +%Y%m%d %H:%M:%S)] $1"
}
log "Starting backup..." # Usage: log "message"
2. Error Handling with set -euo pipefail
Make scripts exit on errors and undefined variables:
#!/bin/bash
set -euo pipefail # Exit on error, undefined var, or failed pipe
# Script will exit if $UNDEFINED_VAR is used (undefined)
echo "$UNDEFINED_VAR" # Fails here!
3. Parse Arguments with getopts
Handle flags (e.g., ./script.sh -v -o output.txt) cleanly:
#!/bin/bash
verbose=0
output_file="output.txt"
# Parse flags: -v (verbose), -o <file> (output)
while getopts "vo:" opt; do
case $opt in
v) verbose=1 ;;
o) output_file="$OPTARG" ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
esac
done
if [ $verbose -eq 1 ]; then
echo "Verbose mode enabled. Outputting to $output_file..."
fi
4. Debugging with set -x
Add set -x to trace execution (shows each command before running):
#!/bin/bash
set -x # Enable debugging
echo "This line will be traced..."
set +x # Disable debugging
echo "This line won't."
Best Practices for Maintainable Scripts
Write scripts that are easy to read, test, and update:
- Comment Liberally: Explain why (not just what) the code does.
- Version Control: Track scripts in Git (e.g., a
bash_scriptsrepo). - Test Thoroughly: Use
echoto preview commands before running withrm/mv. - Secure Sensitive Data: Avoid hardcoding passwords. Use environment variables:
# Load from .env file (chmod 600 .env to secure) source "$HOME/.env" echo "API Key: $MY_SECURE_API_KEY" # From .env - Sanitize Inputs: Validate user input to prevent injection attacks:
read -p "Enter a filename: " filename if [[ ! "$filename" =~ ^[a-zA-Z0-9_.-]+$ ]]; then echo "Invalid filename!" exit 1 fi
Tools to Enhance Bash
These tools will make scripting faster and scripts more reliable:
- ShellCheck: Lints scripts for errors (e.g., undefined variables).
- Install:
sudo apt install shellcheck. - Use:
shellcheck my_script.sh.
- Install:
- shfmt: Formats scripts for consistency (like Prettier for bash).
- Install:
sudo apt install shfmt. - Use:
shfmt -w my_script.sh(auto-fix).
- Install:
- fzf: Fuzzy finder for interactive scripts (e.g., select files to process).
- tput: Add color/style to output (e.g.,
tput setaf 2for green text).
Conclusion
Bash automation is a superpower for Linux users. By starting small (e.g., a backup script) and gradually adding complexity, you’ll transform tedious routines into effortless, reliable workflows. Remember: the best script is one that solves your problem—so adapt examples to fit your needs!
What automations will you build first? Share your scripts in the comments below!