Shell Scripting

Introduction to Shell Scripting

Shell scripting is a powerful way to automate tasks in Unix-like operating systems. It combines command-line tools with programming constructs to create efficient automation solutions.

Key Concepts:

  • Shell types (bash, zsh, sh)
  • Variables and parameters
  • Control structures
  • Functions and modules
  • Input/Output operations
  • Error handling
  • Process management
  • String manipulation

Basic Script Structure

#!/bin/bash

# Script description
# Author: Your Name
# Date: YYYY-MM-DD

# Set strict mode
set -euo pipefail
IFS=$'\n\t'

# Global variables
VERSION="1.0"
CONFIG_FILE="/etc/myapp/config.conf"

# Main function
main() {
    echo "Starting script..."
    check_prerequisites
    process_arguments "$@"
    cleanup
}

# Execute main function
main "$@"

Scripting Basics

Variables and Parameters

# Variable declaration
name="John"
age=25
readonly CONSTANT="value"

# Parameter handling
echo "Script name: $0"
echo "First argument: $1"
echo "All arguments: $@"
echo "Number of arguments: $#"

# Command substitution
current_date=$(date +%Y-%m-%d)
files_count=$(ls -1 | wc -l)

# Arithmetic operations
result=$((5 + 3))
((count++))
let "sum = $a + $b"

# String operations
string="Hello World"
length=${#string}
substring=${string:0:5}
replacement=${string/World/User}

Input/Output

# User input
read -p "Enter your name: " user_name
read -s -p "Password: " password
echo

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

# Output redirection
echo "Log entry" >> app.log
exec 1>output.log    # Redirect stdout
exec 2>error.log     # Redirect stderr

# Here documents
cat << EOF > config.ini
[Settings]
user=$USER
home=$HOME
date=$(date)
EOF

Control Structures

Conditionals

# If statements
if [ "$age" -ge 18 ]; then
    echo "Adult"
elif [ "$age" -ge 13 ]; then
    echo "Teenager"
else
    echo "Child"
fi

# Case statements
case "$choice" in
    y|Y|yes|YES)
        process_yes
        ;;
    n|N|no|NO)
        process_no
        ;;
    *)
        echo "Invalid choice"
        ;;
esac

# Test operators
[ -f "$file" ]     # File exists
[ -d "$dir" ]      # Directory exists
[ -z "$var" ]      # String is empty
[ "$a" -eq "$b" ]  # Numeric equality
[[ "$string" =~ $regex ]]  # Regex match

Loops

# For loop
for i in {1..5}; do
    echo "Count: $i"
done

# Array iteration
for item in "${array[@]}"; do
    process_item "$item"
done

# Directory iteration
for file in *.txt; do
    echo "Processing $file"
done

# While loop
while [ "$count" -lt 10 ]; do
    ((count++))
    echo "$count"
done

# Until loop
until [ "$process" = "stopped" ]; do
    check_process
    sleep 1
done

Functions

Function Definition

# Basic function
greeting() {
    echo "Hello, $1!"
}

# Function with return value
is_number() {
    [[ $1 =~ ^[0-9]+$ ]] && return 0 || return 1
}

# Function with local variables
calculate() {
    local result
    result=$(($1 + $2))
    echo "$result"
}

# Function library
source_file() {
    local file=$1
    if [ -f "$file" ]; then
        source "$file"
        return 0
    else
        echo "Error: $file not found" >&2
        return 1
    fi
}

Error Handling

# Error function
error() {
    local msg=$1
    local code=${2-1}
    echo "Error: $msg" >&2
    exit "$code"
}

# Trap commands
cleanup() {
    rm -f "$TEMPFILE"
}
trap cleanup EXIT

# Error checking
command || error "Command failed"

# Try-catch simulation
{
    risky_command
} || {
    echo "Error caught"
    exit 1
}

Advanced Topics

Process Management

# Background processes
process1 &
process2 &
wait

# Named pipes
mkfifo pipe1
producer > pipe1 &
consumer < pipe1

# Process substitution
diff <(sort file1) <(sort file2)

# Subshells
(
    cd /tmp
    process_files
)

# Job control
jobs
fg %1
bg %2
kill %3

Array Operations

# Array declaration
declare -a numbers=(1 2 3 4 5)
declare -A map=([key1]="value1" [key2]="value2")

# Array operations
numbers+=(6 7)             # Append
length=${#numbers[@]}      # Length
slice=("${numbers[@]:1:3}") # Slice

# Array iteration
for key in "${!map[@]}"; do
    echo "Key: $key, Value: ${map[$key]}"
done

# Array manipulation
sorted=($(sort <<<"${numbers[*]}"))
unique=($(printf "%s\n" "${array[@]}" | sort -u))

Best Practices

Script Structure:

  • Use descriptive shebang line
  • Add script documentation
  • Set strict error handling
  • Use meaningful variable names
  • Structure code in functions
  • Handle errors gracefully
  • Include cleanup routines
  • Add logging functionality

Security:

  • Quote variables properly
  • Validate input data
  • Use secure temporary files
  • Handle secrets securely
  • Set proper permissions
  • Avoid shell injection
  • Use shellcheck
  • Implement access controls

Maintenance:

  • Use version control
  • Write modular code
  • Add comments
  • Include error messages
  • Implement logging
  • Create documentation
  • Test thoroughly
  • Use debugging tools