Your First Shell Script
A shell script is simply a text file containing a sequence of commands that the shell executes in order. Every script starts with a shebang line that tells the system which interpreter to use.
# This is a comment
echo "Hello from LinuxLand!"
echo "Today is $(date +%A), $(date +%B) $(date +%d), $(date +%Y)"
echo "You are logged in as: $(whoami)"
To create and run this script:
nano hello.sh
# Make it executable
chmod +x hello.sh
# Run it
./hello.sh
Variables
Variables store data that your script can reference and manipulate. In Bash, you assign variables without spaces around the equals sign and reference them with a dollar sign.
# Variable assignment (no spaces around =)
NAME="LinuxLand"
VERSION=2026
GREETING="Welcome to $NAME version $VERSION"
echo "$GREETING"
# Command substitution
CURRENT_DIR=$(pwd)
FILE_COUNT=$(ls | wc -l)
echo "Current directory: $CURRENT_DIR"
echo "Files in directory: $FILE_COUNT"
# Read user input
read -p "Enter your name: " USER_NAME
echo "Hello, $USER_NAME!"
"$VAR") to prevent word splitting and globbing issues, especially when variables might contain spaces.Conditionals
Conditionals let your script make decisions and execute different code paths based on conditions.
If/Else Statements
FILE="/etc/nginx/nginx.conf"
if [ -f "$FILE" ]; then
echo "Nginx configuration found."
elif [ -f "/etc/apache2/apache2.conf" ]; then
echo "Apache configuration found."
else
echo "No web server configuration found."
fi
Common Test Operators
[ -f file ] # File exists and is a regular file
[ -d dir ] # Directory exists
[ -r file ] # File is readable
[ -w file ] # File is writable
[ -x file ] # File is executable
# String comparisons
[ "$a" = "$b" ] # Strings are equal
[ "$a" != "$b" ] # Strings are not equal
[ -z "$a" ] # String is empty
# Numeric comparisons
[ "$a" -eq "$b" ] # Equal
[ "$a" -gt "$b" ] # Greater than
[ "$a" -lt "$b" ] # Less than
Loops
For Loops
# Loop over a list
for distro in Ubuntu Fedora Arch Debian; do
echo "Distro: $distro"
done
# Loop over files
for file in /var/log/*.log; do
echo "Log file: $(basename "$file") - Size: $(du -h "$file" | cut -f1)"
done
# C-style for loop
for ((i=1; i<=5; i++)); do
echo "Iteration $i"
done
While Loops
# Count down
COUNT=5
while [ $COUNT -gt 0 ]; do
echo "Countdown: $COUNT"
COUNT=$((COUNT - 1))
sleep 1
done
echo "Done!"
# Read a file line by line
while IFS= read -r line; do
echo "Line: $line"
done < /etc/hosts
Functions
Functions allow you to group reusable blocks of code, making your scripts cleaner and easier to maintain.
# Define a function
check_service() {
local SERVICE=$1
if systemctl is-active --quiet "$SERVICE"; then
echo "[OK] $SERVICE is running"
return 0
else
echo "[FAIL] $SERVICE is not running"
return 1
fi
}
# Use the function
check_service nginx
check_service postgresql
check_service redis
Real-World Script Examples
System Health Check Script
echo "===== System Health Report ====="
echo "Date: $(date)"
echo "Hostname: $(hostname)"
echo ""
echo "--- CPU Load ---"
uptime
echo ""
echo "--- Memory Usage ---"
free -h | head -2
echo ""
echo "--- Disk Usage ---"
df -h / | tail -1 | awk '{print "Used: "$3" / "$2" ("$5" full)"}'
echo ""
# Warn if disk usage exceeds 80%
USAGE=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
if [ "$USAGE" -gt 80 ]; then
echo "WARNING: Disk usage is above 80%!"
fi
Automated Backup Script
BACKUP_DIR="/backups"
SOURCE="/var/www"
DATE=$(date +%Y%m%d_%H%M%S)
FILENAME="backup_${DATE}.tar.gz"
MAX_BACKUPS=7
# Create backup directory if it does not exist
mkdir -p "$BACKUP_DIR"
# Create the backup
echo "Creating backup: $FILENAME"
tar -czf "${BACKUP_DIR}/${FILENAME}" "$SOURCE" 2>/dev/null
if [ $? -eq 0 ]; then
echo "Backup created successfully."
SIZE=$(du -h "${BACKUP_DIR}/${FILENAME}" | cut -f1)
echo "Size: $SIZE"
else
echo "ERROR: Backup failed!"
exit 1
fi
# Remove old backups, keep only the latest
cd "$BACKUP_DIR" || exit
ls -t backup_*.tar.gz | tail -n +$((MAX_BACKUPS + 1)) | xargs -r rm --
echo "Old backups cleaned. Keeping last $MAX_BACKUPS."
Scheduling Scripts with Cron
Cron lets you schedule scripts to run automatically at specific times or intervals.
crontab -e
# Cron format: minute hour day month weekday command
# ┌──── minute (0-59)
# │ ┌──── hour (0-23)
# │ │ ┌──── day of month (1-31)
# │ │ │ ┌──── month (1-12)
# │ │ │ │ ┌──── day of week (0-6, Sunday=0)
# │ │ │ │ │
# * * * * * command
# Run backup daily at 2 AM
0 2 * * * /usr/local/bin/backup.sh
# Run health check every 30 minutes
*/30 * * * * /usr/local/bin/health-check.sh
# Run cleanup every Sunday at midnight
0 0 * * 0 /usr/local/bin/cleanup.sh
Best Practices
- Always start with
#!/bin/bash(or#!/usr/bin/env bashfor portability) - Use
set -euo pipefailat the top to catch errors early and fail on undefined variables - Quote all variables to prevent word splitting and unexpected behaviour
- Use
localvariables inside functions to avoid polluting the global scope - Add error handling for critical operations using
ifchecks ortrap - Log output to files with timestamps for scripts that run unattended
- Test scripts with
bash -n script.sh(syntax check) andshellcheck script.sh(linting)
set -euo pipefail
# Trap errors and clean up
cleanup() {
echo "Cleaning up temporary files..."
rm -f /tmp/myapp_*.tmp
}
trap cleanup EXIT
# Your script logic here
echo "Script running safely with error handling."
Discover essential tools every Linux user needs.
Essential Linux Tools →