language: en kae3g ← back to index
(unlisted) — This essay is not shown on the main index but remains accessible via direct link

kae3g 9601: Shell Scripting Fundamentals - Variables, Conditionals, Loops

Phase 2: Core Systems & Tools | Week 6 | Reading Time: 16 minutes

Welcome to Phase 2! 🎉

Congratulations on completing Phase 1! You now understand the foundations of computing systems. Phase 2 builds on that knowledge by teaching you practical mastery of the tools and systems that power modern infrastructure.

We start with the shell - your most powerful interface to Unix systems.

What You'll Learn

Prerequisites

Why Shell Scripting?

The Unix philosophy (Essay 9510): "Do one thing well" + "Compose tools"

Shell scripts are the glue that combines simple tools into powerful workflows.

What Shell Scripts Do Best

Automation:

#!/bin/bash
# Deploy script - runs every time we ship
./run-tests.sh
./build-artifacts.sh
./upload-to-server.sh
./restart-services.sh

System Administration:

# Check disk space, clean up if needed
if [ $(df / | tail -1 | awk '{print $5}' | sed 's/%//') -gt 90 ]; then
    echo "Disk almost full! Cleaning..."
    find /tmp -type f -mtime +7 -delete
fi

Data Processing:

# Process logs: extract errors, count, email report
grep ERROR /var/log/app.log | \
    cut -d' ' -f3 | \
    sort | uniq -c | \
    mail -s "Error Report" admin@example.com

Quick Prototypes:

Your First Script

The Shebang

#!/bin/bash
# This is a comment

echo "Hello, Valley Builder!"

First line (#!/bin/bash): Shebang

Make it executable:

chmod +x hello.sh
./hello.sh
# Output: Hello, Valley Builder!

Variables

Basic Assignment

name="Ada Lovelace"
year=1842

echo "Hello, $name!"
echo "The year is $year"

Rules:

Quoting

Three types:

# Single quotes: Literal (no expansion)
echo 'My name is $name'
# Output: My name is $name

# Double quotes: Variable expansion
echo "My name is $name"
# Output: My name is Ada Lovelace

# No quotes: Word splitting + globbing
files=$HOME/*.txt
echo $files
# Output: /home/user/file1.txt /home/user/file2.txt

Best practice: Always quote variables!

# BAD (breaks on spaces)
file=$HOME/My Documents/file.txt
cat $file  # ERROR: tries to cat 3 files

# GOOD
file="$HOME/My Documents/file.txt"
cat "$file"  # Works!

Command Substitution

Run command, capture output:

# Old style (deprecated)
current_user=`whoami`

# New style (preferred)
current_user=$(whoami)
current_date=$(date +%Y-%m-%d)

echo "User: $current_user"
echo "Date: $current_date"

Conditionals

if/then/else

#!/bin/bash

age=25

if [ $age -ge 18 ]; then
    echo "Adult"
else
    echo "Minor"
fi

Syntax:

Test Operators

Numeric:

[ $a -eq $b ]  # Equal
[ $a -ne $b ]  # Not equal
[ $a -lt $b ]  # Less than
[ $a -le $b ]  # Less than or equal
[ $a -gt $b ]  # Greater than
[ $a -ge $b ]  # Greater than or equal

String:

[ "$a" = "$b" ]   # Equal (use = not ==!)
[ "$a" != "$b" ]  # Not equal
[ -z "$a" ]       # Empty string
[ -n "$a" ]       # Not empty

File:

[ -e "$file" ]  # Exists
[ -f "$file" ]  # Regular file
[ -d "$file" ]  # Directory
[ -r "$file" ]  # Readable
[ -w "$file" ]  # Writable
[ -x "$file" ]  # Executable

Multiple Conditions

# AND (both must be true)
if [ $age -ge 18 ] && [ $country = "USA" ]; then
    echo "Can vote in USA"
fi

# OR (either can be true)
if [ $age -lt 13 ] || [ $age -gt 65 ]; then
    echo "Discounted ticket"
fi

# NOT
if [ ! -f "$file" ]; then
    echo "File does not exist"
fi

elif (else if)

#!/bin/bash

score=85

if [ $score -ge 90 ]; then
    grade="A"
elif [ $score -ge 80 ]; then
    grade="B"
elif [ $score -ge 70 ]; then
    grade="C"
else
    grade="F"
fi

echo "Grade: $grade"

Loops

for Loop

Iterate over list:

#!/bin/bash

# Loop over words
for fruit in apple banana cherry; do
    echo "I like $fruit"
done

# Loop over files
for file in *.txt; do
    echo "Processing $file"
    wc -l "$file"
done

# Loop over numbers (Bash-specific)
for i in {1..5}; do
    echo "Count: $i"
done

C-style for loop (Bash):

for ((i=0; i<10; i++)); do
    echo "Number: $i"
done

while Loop

Repeat while condition true:

#!/bin/bash

count=1

while [ $count -le 5 ]; do
    echo "Count: $count"
    count=$((count + 1))
done

Read file line by line:

#!/bin/bash

while IFS= read -r line; do
    echo "Line: $line"
done < input.txt

Explanation:

until Loop

Repeat until condition true (opposite of while):

#!/bin/bash

count=1

until [ $count -gt 5 ]; do
    echo "Count: $count"
    count=$((count + 1))
done

Use case: Retry until success

#!/bin/bash

until ping -c 1 example.com &> /dev/null; do
    echo "Waiting for network..."
    sleep 1
done

echo "Network is up!"

Exit Codes

Every command returns an exit code:

Check last exit code:

ls /nonexistent
echo $?  # Prints: 2 (error)

ls /home
echo $?  # Prints: 0 (success)

Using Exit Codes

#!/bin/bash

if grep "ERROR" /var/log/app.log; then
    echo "Found errors in log"
    exit 1  # Signal failure
else
    echo "No errors found"
    exit 0  # Signal success
fi

Short-Circuit Operators

&& = "and then" (run if previous succeeded):

cd /tmp && rm tempfile
# Only runs rm if cd succeeds

|| = "or else" (run if previous failed):

mkdir /var/myapp || exit 1
# Exit if mkdir fails

Combine:

cd /project && make && make test && echo "Success!"
# Each step must succeed

Functions

Define reusable code:

#!/bin/bash

# Define function
greet() {
    echo "Hello, $1!"
}

# Call function
greet "Alice"
greet "Bob"

With return value:

#!/bin/bash

is_even() {
    local num=$1
    if [ $((num % 2)) -eq 0 ]; then
        return 0  # True (success)
    else
        return 1  # False (failure)
    fi
}

# Use in conditional
if is_even 4; then
    echo "4 is even"
fi

if is_even 7; then
    echo "7 is even"
else
    echo "7 is odd"
fi

Note: return sets exit code, not value. To return a value, use echo:

add() {
    echo $(($1 + $2))
}

result=$(add 3 5)
echo "3 + 5 = $result"

Error Handling

set -e (Exit on Error)

#!/bin/bash
set -e  # Exit immediately if any command fails

cd /project
make
make test
make install

echo "All steps succeeded!"

Without set -e: Script continues even if make fails!

set -u (Error on Undefined Variable)

#!/bin/bash
set -u  # Exit if using undefined variable

echo $UNDEFINED_VAR  # ERROR: unbound variable

Best practice: Start scripts with:

#!/bin/bash
set -euo pipefail

Explanation:

trap (Cleanup on Exit)

#!/bin/bash

cleanup() {
    echo "Cleaning up..."
    rm -f /tmp/tempfile
}

trap cleanup EXIT  # Run cleanup on exit

# Script continues...
touch /tmp/tempfile
# ... do work ...

# cleanup() runs automatically on exit (success or failure!)

Try This

Exercise 1: Backup Script

Write a script that:

Exercise 2: System Monitor

Write a script that:

Exercise 3: Batch Rename

Write a script that:

Best Practices

1. Always Quote Variables

# BAD
cat $file

# GOOD
cat "$file"

Why: Prevents word splitting and globbing.

2. Use Meaningful Names

# BAD
x=10
f=$1

# GOOD
max_retries=10
config_file=$1

3. Check Arguments

#!/bin/bash

if [ $# -lt 1 ]; then
    echo "Usage: $0 <filename>"
    exit 1
fi

file="$1"

4. Use set -euo pipefail

Catch errors early, don't continue on failure.

5. Provide Help

#!/bin/bash

if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
    cat << EOF
Usage: $0 [OPTIONS] <file>

Options:
  -h, --help     Show this help
  -v, --verbose  Verbose output

Examples:
  $0 myfile.txt
  $0 -v myfile.txt
EOF
    exit 0
fi

6. Log Actions

#!/bin/bash

log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*"
}

log "Starting backup..."
# ... do backup ...
log "Backup complete!"

When NOT to Use Shell

Shell is great for:

Shell is bad for:

Rule of thumb: If script > 100 lines, consider a "real" language.

Going Deeper

Related Essays

External Resources

Reflection Questions

  1. Why prefer shell over Python for system tasks? (Speed to write, installed everywhere)
  2. When is quoting critical? (Filenames with spaces, preventing injection attacks)
  3. Why set -e controversial? (Some argue it hides errors in conditionals—use carefully!)
  4. Should shell scripts have tests? (YES! Use BATS or shUnit2)
  5. How does shell relate to Nock? (Both are minimal, composable—shell glues Unix, Nock specifies computation!)

Summary

Shell scripting is:

Core concepts:

Best practices:

When to use:

In the Valley:

Next: Essay 9603 - Shell Text Processing! We'll master grep, sed, and awk—the power trio for text manipulation!

Navigation:
← Previous: 9600 (Phase 1 Synthesis) | Phase 2 Index | Next: 9603 (Shell Text Processing) (New!)

Metadata:

Copyright © 2025 kae3g | Dual-licensed under Apache-2.0 / MIT
Competitive technology in service of clarity and beauty

View Hidden Docs Index | Return to Main Index


← back to index