Linux Full Tutorial -- Part 7: Shell Scripting

By Suraj Ahir 2025-12-09 11 min read

← Part 6Linux Tutorial · Part 7 of 12Part 8 →
Linux Full Tutorial -- Part 7: Shell Scripting

Shell scripting is the automation superpower of Linux. Every task you do repeatedly in the terminal is a candidate for a script. Backup files, deploy applications, restart services, clean up logs, check disk space and alert if low -- all of these are 10-50 line bash scripts that run automatically and save hours of manual work every week.

I still remember the first time I wrote a script that automated our deployment process. What took 15 manual steps and 20 minutes of careful attention became a 30-second automated process. That script ran thousands of times without a single manual mistake. That is the power of shell scripting.

Script Basics

Structure of a bash script
#!/bin/bash
# The shebang line tells the OS which interpreter to use

# Comments explain what the script does

echo "Script started at $(date)"

# Variables
APP_NAME="myapp"
DEPLOY_DIR="/home/ubuntu/${APP_NAME}"
LOG_FILE="/var/log/${APP_NAME}/deploy.log"

echo "Deploying ${APP_NAME} to ${DEPLOY_DIR}"
Make executable and run
chmod +x deploy.sh     # Make executable
./deploy.sh            # Run from current directory
bash deploy.sh         # Run with bash explicitly
/home/ubuntu/deploy.sh # Run with full path

Variables and Input

Variables and command-line arguments
#!/bin/bash

# Positional arguments
ENVIRONMENT=$1       # First argument
VERSION=$2           # Second argument

# Special variables
echo "Script name: $0"
echo "All args: $@"
echo "Arg count: $#"
echo "Last exit code: $?"
echo "Process ID: $$"

# Command substitution
CURRENT_DATE=$(date +%Y-%m-%d)
DISK_USAGE=$(df -h / | tail -1 | awk "{print $5}")

# Read user input
read -p "Enter deployment environment: " ENV
read -sp "Enter password: " PASSWORD  # -s = silent

Conditionals

if-elif-else in bash
#!/bin/bash

ENV=$1

if [ -z "$ENV" ]; then
    echo "Error: Environment required"
    echo "Usage: $0 "
    exit 1
fi

if [ "$ENV" == "production" ]; then
    echo "Deploying to production"
    DEPLOY_HOST="prod.example.com"
elif [ "$ENV" == "staging" ]; then
    echo "Deploying to staging"
    DEPLOY_HOST="staging.example.com"
else
    echo "Unknown environment: $ENV"
    exit 1
fi

# File checks
[ -f "/etc/nginx/nginx.conf" ] && echo "Nginx config exists"
[ -d "/var/log/app" ] || mkdir -p /var/log/app
[ -r "$FILE" ] && echo "File is readable"

# Numeric comparison
DISK_PCT=85
if [ $DISK_PCT -gt 90 ]; then
    echo "CRITICAL: Disk almost full"
elif [ $DISK_PCT -gt 80 ]; then
    echo "WARNING: Disk usage high"
fi

Loops

for and while loops
# for loop over list
for server in web1 web2 web3; do
    echo "Deploying to $server"
    ssh ubuntu@$server "sudo systemctl restart myapp"
done

# for loop with range
for i in $(seq 1 5); do
    echo "Attempt $i"
done

# for loop over files
for logfile in /var/log/nginx/*.log; do
    echo "Processing: $logfile"
    gzip "$logfile"
done

# while loop
ATTEMPT=0
while [ $ATTEMPT -lt 3 ]; do
    curl -sf http://localhost/health && break
    ATTEMPT=$((ATTEMPT + 1))
    echo "Health check failed, attempt $ATTEMPT"
    sleep 5
done

# Read file line by line
while IFS= read -r line; do
    echo "Processing: $line"
done < servers.txt

Functions

Reusable functions in bash
#!/bin/bash

# Function definitions
log() {
    echo "[$(date +%H:%M:%S)] $1" | tee -a /var/log/deploy.log
}

check_health() {
    local url=$1
    local max_attempts=5
    local attempt=0
    
    while [ $attempt -lt $max_attempts ]; do
        if curl -sf "$url/health" > /dev/null 2>&1; then
            log "Health check passed"
            return 0
        fi
        attempt=$((attempt + 1))
        log "Health check failed ($attempt/$max_attempts)"
        sleep 10
    done
    return 1
}

# Call functions
log "Starting deployment"
if ! check_health "http://localhost:8000"; then
    log "ERROR: Service unhealthy after deployment"
    exit 1
fi

Error Handling

Writing robust scripts
#!/bin/bash
set -e          # Exit on any error
set -u          # Error on undefined variables
set -o pipefail # Pipes fail if any command fails

# Trap for cleanup on exit
cleanup() {
    echo "Cleaning up..."
    rm -f /tmp/deploy.lock
}
trap cleanup EXIT
trap "echo ERROR: Script failed on line $LINENO; exit 1" ERR

# Check prerequisites
command -v docker >> /dev/null || { echo "Docker not found"; exit 1; }

# Safe variable expansion
echo "${VARIABLE:-default_value}"  # Use default if unset

Frequently Asked Questions

What is the shebang line in a bash script?

#!/bin/bash is the shebang -- it tells the OS which interpreter to use when executing the script directly. Without it, the script runs in your current shell which may not be bash. Always include it as the first line of every bash script.

How do I debug a bash script?

bash -x script.sh runs the script with execution trace -- shows each command before running it. Add set -x inside the script to enable tracing from that point. set -e makes the script exit on any error, which is good practice for production scripts.

What is the difference between $@ and $*?

"$@" expands to separate quoted arguments: "$1" "$2" "$3". "$*" expands to a single string: "$1 $2 $3". Use "$@" when passing arguments to another command to preserve argument boundaries with spaces.

How do I check if a command succeeded in bash?

Check $? (exit code): 0 = success, non-zero = failure. Use && for run-if-success: command && echo "OK". Use || for run-if-fail: command || exit 1. Use if command; then ... to check in a conditional.

How do I schedule a bash script to run automatically?

Use cron for time-based scheduling. crontab -e opens your crontab. "0 2 * * * /home/ubuntu/backup.sh" runs backup.sh at 2am daily. Or use systemd timers for more advanced scheduling with better logging and dependency management.

In Part 8, we cover cron jobs -- scheduling your scripts to run automatically at specific times.

Key takeaways

Continue reading
Part 8 — Bash Scripting
Automate before automation tools find you.
Suraj Ahir — author of SRJahir Tech

Written by

Suraj Ahir

Cloud & DevOps engineer running four live production services on my own AWS infrastructure. I write everything on this site myself — no ghostwriters, no AI filler.

← Part 6Linux Tutorial · Part 7 of 12Part 8 →
← Back to Blog
Disclaimer: Educational content only. No guarantees.