Table of Contents
-
- 1.1 Why Automate with Bash?
- 1.2 Common Use Cases
-
Setting Up Your Bash Environment
- 2.1 Checking Your Bash Version
- 2.2 Choosing a Text Editor
- 2.3 Making Scripts Executable
-
Core Bash Concepts for Automation
- 3.1 Variables: Storing Data
- 3.2 Input/Output Redirection
- 3.3 Conditionals (if-else)
- 3.4 Loops (for, while)
- 3.5 Functions: Reusable Code Blocks
-
- 4.1 File Management: Backup & Cleanup
- 4.2 System Monitoring & Alerts
- 4.3 Network Automation: Pinging Hosts
- 4.4 Scheduling Tasks with Cron
-
- 5.1 Error Handling (set -e, traps)
- 5.2 Logging: Tracking Script Activity
- 5.3 Command Substitution
- 5.4 Arrays: Managing Lists of Data
- 5.5 Using External Tools (awk, sed, jq)
-
Best Practices for Bash Scripts
- 6.1 Commenting & Documentation
- 6.2 Testing & Debugging
- 6.3 Security: Avoiding Common Pitfalls
- 6.4 Portability: Writing Cross-Distribution Scripts
-
- 7.1 Permission Denied Errors
- 7.2 Syntax Errors: Missing Semicolons & Spaces
- 7.3 Debugging with
set -x - 7.4 Checking Exit Codes
What is Bash Automation?
Bash automation is the practice of writing Bash scripts (text files containing a sequence of commands) to automate repetitive or complex tasks. Unlike manual command-line input, scripts execute predefined steps consistently, reducing human error and saving time.
1.1 Why Automate with Bash?
- Speed: Scripts run faster than manual typing, especially for multi-step tasks.
- Consistency: Eliminates typos and ensures tasks are executed the same way every time.
- Scalability: Automate tasks across multiple machines (e.g., via SSH).
- Accessibility: Bash is preinstalled on all Linux systems—no extra tools required.
1.2 Common Use Cases
- Backing up files/databases.
- Cleaning up log files or temporary directories.
- Monitoring system resources (CPU, disk space) and sending alerts.
- Deploying software or updating packages.
- Processing text data (e.g., parsing logs with
grep/awk).
Setting Up Your Bash Environment
Before writing scripts, let’s configure your environment for success.
2.1 Checking Your Bash Version
Ensure you’re using Bash (not another shell like Zsh or Sh). Run:
echo $SHELL # Output: /bin/bash (if Bash is default)
bash --version # Check version (e.g., GNU bash, version 5.1.16)
2.2 Choosing a Text Editor
Scripts are plain text files—use an editor that highlights syntax for readability:
- Nano: Simple, beginner-friendly (
nano script.sh). - Vim/Neovim: Powerful, keyboard-driven (
vim script.sh). - VS Code: With the “Bash IDE” extension for linting/debugging.
2.3 Making Scripts Executable
Scripts must be marked executable to run. Follow these steps:
- Create a script file (e.g.,
my_script.sh). - Add a “shebang” line at the top to specify the shell:
#!/bin/bash # Tells the system to run this with Bash - Make it executable with
chmod:chmod +x my_script.sh - Run it:
./my_script.sh # (./ is required if the script is in the current directory)
Core Bash Concepts for Automation
Master these basics to write effective scripts.
3.1 Variables: Storing Data
Variables hold data for reuse. Declare them without spaces:
name="Alice"
age=30
# Access variables with $
echo "Hello, $name! You are $age years old." # Output: Hello, Alice! You are 30 years old.
Environment Variables: Predefined variables like $HOME (your home directory) or $PATH (executable search path). Use env to list them.
3.2 Input/Output Redirection
Control where command output goes (or input comes from):
>: Overwrite a file with output (e.g.,echo "Hi" > output.txt).>>: Append to a file (e.g.,echo "Again" >> output.txt).<: Read input from a file (e.g.,sort < unsorted.txt).|(Pipe): Send output of one command to another (e.g.,ls -l | grep ".txt").
3.3 Conditionals (if-else)
Execute code based on conditions. Use [ ] (test command) for checks:
file="data.txt"
if [ -f "$file" ]; then # -f checks if $file is a regular file
echo "$file exists!"
elif [ -d "$file" ]; then # -d checks if directory
echo "$file is a directory!"
else
echo "$file does NOT exist."
fi
Common Conditions:
-e $file: File exists (any type).-s $file: File exists and is not empty.$a -eq $b: Numeric equality (e.g.,5 -eq 5)."$str1" = "$str2": String equality.
3.4 Loops: Repeating Actions
For Loops: Iterate Over Lists
Loop through files, numbers, or strings:
# Loop through files in the current directory
for file in *.txt; do
echo "Processing $file..."
cat "$file" # Display file contents
done
# Loop through numbers (1 to 5)
for i in {1..5}; do
echo "Count: $i"
done
While Loops: Run Until a Condition Fails
Read lines from a file or prompt for input:
# Read lines from a file
while IFS= read -r line; do # IFS= prevents trimming whitespace
echo "Line: $line"
done < "input.txt"
# Prompt user until they enter "quit"
while true; do
read -p "Enter a command (or 'quit' to exit): " cmd
if [ "$cmd" = "quit" ]; then
break # Exit loop
fi
echo "You entered: $cmd"
done
3.5 Functions: Reusable Code Blocks
Group commands into functions for readability and reuse:
greet() {
local name=$1 # $1 = first argument to the function
echo "Hello, $name!"
}
greet "Bob" # Output: Hello, Bob!
greet "Alice" # Output: Hello, Alice!
Return Values: Use return for exit codes (0 = success) or echo to return strings:
add() {
echo $(( $1 + $2 )) # Echo the sum
}
result=$(add 3 5) # Capture output with command substitution
echo "3 + 5 = $result" # Output: 3 + 5 = 8
Practical Automation Examples
Let’s apply core concepts to real-world tasks.
4.1 File Management: Backup & Cleanup
Example 1: Timestamped Backup Script
Create a backup of a directory with a timestamp (e.g., docs_backup_20240520):
#!/bin/bash
# backup_docs.sh: Backup ~/Documents to ~/backups with timestamp
SOURCE_DIR="$HOME/Documents"
BACKUP_DIR="$HOME/backups"
TIMESTAMP=$(date +%Y%m%d_%H%M%S) # Format: YYYYMMDD_HHMMSS
BACKUP_NAME="docs_backup_$TIMESTAMP.tar.gz"
# Create backup directory if it doesn't exist
mkdir -p "$BACKUP_DIR"
# Backup and compress
tar -czf "$BACKUP_DIR/$BACKUP_NAME" -C "$SOURCE_DIR" .
# Check if backup succeeded
if [ $? -eq 0 ]; then # $? = exit code of last command (0 = success)
echo "Backup successful: $BACKUP_DIR/$BACKUP_NAME"
else
echo "Backup FAILED!"
exit 1 # Exit with error code 1
fi
Run it:
chmod +x backup_docs.sh
./backup_docs.sh
Example 2: Clean Up Old Logs
Delete log files older than 7 days in /var/log:
#!/bin/bash
# clean_logs.sh: Delete logs older than 7 days
LOG_DIR="/var/log"
DAYS=7
# Use find to locate and delete files
find "$LOG_DIR" -name "*.log" -type f -mtime +$DAYS -delete
echo "Deleted logs older than $DAYS days in $LOG_DIR"
4.2 System Monitoring & Alerts
Example: Disk Space Alert
Send an email if disk usage exceeds 90%:
#!/bin/bash
# disk_alert.sh: Alert on high disk usage
THRESHOLD=90 # Percentage threshold
EMAIL="[email protected]"
MOUNT_POINT="/" # Monitor root filesystem
# Get disk usage percentage (e.g., "85" for 85%)
USAGE=$(df -P "$MOUNT_POINT" | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
echo "WARNING: Disk usage on $MOUNT_POINT is $USAGE% (Threshold: $THRESHOLD%)" | mail -s "Disk Alert: $MOUNT_POINT" "$EMAIL"
fi
Note: Install mailutils first for email support: sudo apt install mailutils (Debian/Ubuntu) or sudo dnf install mailx (Fedora/RHEL).
4.3 Network Automation: Ping Multiple Hosts
Check connectivity to a list of servers:
#!/bin/bash
# ping_hosts.sh: Ping a list of hosts and log results
HOSTS=("google.com" "github.com" "192.168.1.1")
LOG_FILE="ping_log.txt"
echo "Ping results: $(date)" > "$LOG_FILE" # Overwrite log with timestamp
for host in "${HOSTS[@]}"; do
if ping -c 1 -W 2 "$host" > /dev/null; then # -c 1 = 1 packet, -W 2 = 2s timeout
echo "$host: UP" >> "$LOG_FILE"
else
echo "$host: DOWN" >> "$LOG_FILE"
fi
done
cat "$LOG_FILE" # Display results
4.4 Scheduling Tasks with Cron
Use cron to run scripts automatically (e.g., daily backups).
Cron Syntax
Cron jobs are defined in crontab with this format:
* * * * * command_to_run
- - - - -
| | | | |
| | | | +-- Day of week (0=Sun, 6=Sat)
| | | +---- Month (1-12)
| | +------ Day of month (1-31)
| +-------- Hour (0-23)
+---------- Minute (0-59)
Example: Run Backup Daily at 2 AM
- Open crontab editor:
crontab -e - Add this line:
0 2 * * * /home/youruser/backup_docs.sh >> /home/youruser/backup_log.txt 2>&10 2 * * *: Run at 2:00 AM daily.>> backup_log.txt 2>&1: Append output/errors to log.
Advanced Bash Techniques
Take your scripts to the next level with these pro tips.
5.1 Error Handling: set -euo pipefail
Make scripts exit on errors, unset variables, or failed pipes:
#!/bin/bash
set -euo pipefail # Exit on:
# -e: Error (non-zero exit code)
# -u: Undefined variable
# -o pipefail: Failed pipe (e.g., cmd1 | cmd2 fails if cmd1 fails)
# Example: This will exit if "nonexistent_file.txt" doesn't exist
cat nonexistent_file.txt # Script exits here with error
5.2 Logging: Track Script Activity
Log messages with timestamps to a file:
#!/bin/bash
LOG_FILE="/var/log/my_script.log"
log() {
echo "[$(date +%Y-%m-%d %H:%M:%S)] $1" >> "$LOG_FILE"
}
log "Script started"
log "Processing data..."
# ... (script logic) ...
log "Script finished"
5.3 Arrays: Manage Lists
Store multiple values in a single variable:
#!/bin/bash
fruits=("apple" "banana" "cherry")
# Print all elements
echo "All fruits: ${fruits[@]}" # Output: apple banana cherry
# Loop through array
for fruit in "${fruits[@]}"; do
echo "Fruit: $fruit"
done
# Get array length
echo "Number of fruits: ${#fruits[@]}" # Output: 3
5.4 Using External Tools
Combine Bash with tools like awk (text processing) or jq (JSON parsing):
Example: Parse JSON with jq
If data.json contains {"name": "Alice", "age": 30}, extract the name:
name=$(jq -r '.name' data.json)
echo "Name: $name" # Output: Name: Alice
Best Practices for Bash Scripts
6.1 Commenting
Explain why (not just what) your code does:
#!/bin/bash
# backup.sh: Backup user data (critical for disaster recovery)
# Usage: ./backup.sh <source> <dest>
SOURCE="$1"
DEST="$2"
# Validate inputs (prevent accidental overwrites)
if [ -z "$SOURCE" ] || [ -z "$DEST" ]; then
echo "Error: Missing source or destination!"
echo "Usage: $0 <source> <dest>"
exit 1
fi
6.2 Testing with shellcheck
Use shellcheck (a linter) to catch bugs:
sudo apt install shellcheck # Install
shellcheck my_script.sh # Analyze script
6.3 Security: Avoid Risks
- Never use
evalwith untrusted input (risk of code injection). - Quote variables to prevent word splitting:
"$var"instead of$var. - Avoid running scripts as root unless necessary.
Troubleshooting Bash Scripts
7.1 Permission Denied
Issue: ./script.sh: Permission denied
Fix: Make the script executable: chmod +x script.sh
7.2 Syntax Error: “unexpected end of file”
Issue: Missing fi (for if), done (for loops), or closing braces.
Fix: Use indent or an IDE to auto-format and check structure.
7.3 Debugging with set -x
Enable debug mode to see every command executed:
#!/bin/bash
set -x # Print commands as they run
var="hello"
echo "$var" # Output: + echo hello; hello
Conclusion
Bash automation is a cornerstone of Linux productivity. With the concepts in this guide—variables, loops, functions, and practical examples—you can automate everything from simple file backups to complex system monitoring.
Start small: write a script to automate your most repetitive task today. Over time, you’ll build a library of scripts that transform how you interact with Linux.
References
- GNU Bash Manual
- Crontab Guru – Test cron schedules.
- ShellCheck – Online Bash linter.
- Bash Hackers Wiki – Advanced tips.
- jq Documentation – JSON parsing.