Bash Scripting Fundamentals

Bash scripting transforms repetitive manual tasks into reliable, automated processes. As a sysadmin, the most valuable scripts you write are not fancy programs — they are simple, readable sequences of commands that run consistently without human attention. A well-written 20-line bash script that creates user accounts, configures firewall rules, and sends a confirmation email saves hours of manual work every week and eliminates human error.

Why bash scripting for sysadmins?

Bash scripting use cases for sysadmins:
  Automation:   deploy apps, configure servers, create users
  Monitoring:   check disk space, alert on high load, watch log files
  Maintenance:  rotate logs, clean temp files, backup databases
  Reporting:    daily system summaries, resource usage reports
  Integration:  chain tools together, parse command output, call APIs

Script structure and variables

nano /usr/local/bin/backup-check.sh

backup-check.sh — basic script structure

#!/bin/bash
# Always start with shebang — tells OS which interpreter to use
# Use set -euo pipefail for safer scripts:
set -e    # Exit immediately if any command fails
set -u    # Treat unset variables as errors
set -o pipefail    # Catch pipe failures (cmd1 | cmd2 — if cmd1 fails, fail the script)

# Variables:
BACKUP_DIR="/var/backups/daily"
RETENTION_DAYS=7
DATE=$(date +%Y-%m-%d)         # Command substitution
BACKUP_FILE="${BACKUP_DIR}/backup-${DATE}.tar.gz"

# Quote variables to handle spaces:
echo "Creating backup: ${BACKUP_FILE}"

# Read-only constants:
readonly LOG_FILE="/var/log/backup.log"

# Logging function (used throughout):
log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" | tee -a "${LOG_FILE}"
}
chmod +x /usr/local/bin/backup-check.sh
/usr/local/bin/backup-check.sh

Conditionals and loops

nano /usr/local/bin/disk-alert.sh

disk-alert.sh — conditionals and loops

#!/bin/bash
set -euo pipefail

THRESHOLD=85
ALERT_EMAIL="admin@example.com"

# Loop over all mounted filesystems:
while IFS= read -r line; do
    # Parse df output:
    usage=$(echo "$line" | awk '{print $5}' | tr -d '%')
    mountpoint=$(echo "$line" | awk '{print $6}')

    # Conditional check:
    if [[ "$usage" -gt "$THRESHOLD" ]]; then
        echo "ALERT: ${mountpoint} is ${usage}% full" |             mail -s "Disk Alert on $(hostname)" "${ALERT_EMAIL}"
        echo "Sent alert for ${mountpoint}: ${usage}%"
    fi
done < <(df -h | tail -n +2)    # Process substitution: feed df output as input
# Common conditional patterns:
# [[ -f /path/file ]]  → file exists
# [[ -d /path/dir ]]   → directory exists
# [[ -z "$var" ]]      → variable is empty
# [[ -n "$var" ]]      → variable is non-empty
# [[ "$a" == "$b" ]]   → string equality
# [[ "$n" -gt 100 ]]   → numeric greater than

Functions and arguments

nano /usr/local/bin/create-user.sh

create-user.sh — functions and argument handling

#!/bin/bash
set -euo pipefail

# $0 = script name, $1 = first arg, $2 = second arg, $# = arg count
usage() {
    echo "Usage: $0  "
    echo "Example: $0 irfan developers"
    exit 1
}

# Validate argument count:
if [[ $# -ne 2 ]]; then
    usage
fi

USERNAME="$1"
GROUP="$2"

create_user() {
    local username="$1"    # local = scoped to function
    local group="$2"

    if id "${username}" &>/dev/null; then
        echo "User ${username} already exists"
        return 0
    fi

    useradd -m -G "${group}" -s /bin/bash "${username}"
    passwd "${username}"
    echo "Created user: ${username} in group: ${group}"
}

create_user "${USERNAME}" "${GROUP}"
sudo /usr/local/bin/create-user.sh irfan developers

Conclusion

The three most important bash scripting practices: always start scripts with set -euo pipefail so failures are caught immediately rather than silently continuing; quote every variable as "${var}" to handle spaces and special characters; and add a usage() function with argument validation so scripts fail loudly with a helpful message rather than failing mysteriously mid-execution. A script that catches its own errors is far more valuable than one that silently does the wrong thing.

FAQ

Why should administrators understand Bash Scripting Fundamentals?+

Because this topic affects planning decisions, server lifecycle, compatibility, support expectations, or how you reason about Ubuntu systems before making operational changes.

Do I need a lab for this topic?+

A lab is useful for checking commands and seeing the concept on a real Ubuntu machine, but the main value is understanding the decision, tradeoff, or system behavior clearly.

How should I use this knowledge in production?+

Use it to make better choices, document why those choices were made, and avoid rushed changes that ignore support windows, compatibility, stability, or operational risk.

Need help with Ubuntu administration?

Work directly with Muhammad Irfan Aslam for Ubuntu Server, Linux, cloud, Docker, DevOps, CI/CD, or infrastructure troubleshooting support.

Hire Me for Support