Syntax
Chapter 3: Basic Syntax and First Script
Section titled “Chapter 3: Basic Syntax and First Script”Overview
Section titled “Overview”This chapter covers the fundamental syntax elements of bash scripting. You’ll learn how to create, execute, and structure bash scripts properly. We’ll build a foundation that will support more advanced topics throughout this guide.
Script Structure
Section titled “Script Structure”Anatomy of a Bash Script
Section titled “Anatomy of a Bash Script”Every bash script follows a specific structure. Understanding this structure is essential for writing professional scripts.
┌─────────────────────────────────────────────────────────────────────┐│ BASH SCRIPT STRUCTURE │└─────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────┐│ Line 1: Shebang ││ #!/usr/bin/env bash ││ │ ││ └── Tells the system which interpreter to use ││ - #! is called "shebang" ││ - Can also use: #!/bin/bash ││ - #!/usr/bin/env bash is more portable │└───────────────────────────────────────────────────────────────────────┘┌───────────────────────────────────────────────────────────────────────┐│ Lines 2+: Comments (optional but recommended) ││ # ││ # Script Name: myscript.sh ││ # Description: This script does something useful ││ # Author: Your Name ││ # Date: 2024-01-15 ││ # ││ Comments help: ││ - Document the script's purpose ││ - Explain complex logic ││ - Make code maintainable │└───────────────────────────────────────────────────────────────────────┘┌───────────────────────────────────────────────────────────────────────┐│ Lines 3+: Script Body ││ ││ set -euo pipefail # Error handling (explained later) ││ ││ VARIABLES # Variable declarations ││ ││ FUNCTIONS # Function definitions ││ ││ MAIN LOGIC # The actual script execution │└───────────────────────────────────────────────────────────────────────┘┌───────────────────────────────────────────────────────────────────────┐│ Optional: Exit Status ││ exit 0 # 0 = success, non-zero = error │└───────────────────────────────────────────────────────────────────────┘Shebang Explained
Section titled “Shebang Explained”The shebang (#!) is the first line that tells the operating system which interpreter to use to execute the script.
# Common shebang variations
# 1. Using absolute path to bash (most common)#!/bin/bash
# 2. Using env to find bash (more portable)#!/usr/bin/env bash
# 3. Using absolute path to env#!/usr/env/bin/bash
# 4. For specific bash version (if needed)#!/bin/bash -l
# 5. With login shell options#!/usr/bin/env bash -lWhy #!/usr/bin/env bash is preferred:
- Automatically finds bash in the PATH
- Works across different Linux distributions
- Works on macOS, BSD, and other Unix-like systems
- More flexible than hardcoded paths
Comments in Bash
Section titled “Comments in Bash”Single Line Comments
Section titled “Single Line Comments”# This is a single line commentecho "Hello, World!" # This is an inline comment
# ============================================================================# MULTI-LINE COMMENT BLOCK (using series of #)# You can create comment blocks like this for section headers# This is useful for documentation within scripts# ============================================================================Multi-line Comments (Here Document Trick)
Section titled “Multi-line Comments (Here Document Trick)”# Method 1: Use a colon with a here document: <<'COMMENT'This is a multi-line comment block.It can span multiple lines.Variables like $HOME won't be expanded here.Useful for disabling code temporarily.COMMENT
# Method 2: With expansion: <<COMMENTThis is another multi-line comment.Variables like $HOME will be expanded: $HOMEUseful for generating documentation.COMMENTYour First Script
Section titled “Your First Script”Creating “Hello World”
Section titled “Creating “Hello World””Let’s create your first bash script step by step.
# Step 1: Create the script filecat > /tmp/hello_world.sh << 'EOF'#!/usr/bin/env bash
# hello_world.sh - Your first bash script# This script prints "Hello, World!" to the console
echo "Hello, World!"EOF
# Step 2: Make it executablechmod +x /tmp/hello_world.sh
# Step 3: Run it/tmp/hello_world.shA More Practical Script
Section titled “A More Practical Script”Let’s create a script that’s actually useful for DevOps work.
#!/usr/bin/env bash#===============================================================================# system_info.sh - Display system information#===============================================================================
# Script description:# This script displays basic system information that DevOps engineers# commonly need to check when working with servers.
echo "=============================================="echo " SYSTEM INFORMATION REPORT"echo "=============================================="echo ""
echo "Hostname: $(hostname)"echo "Kernel: $(uname -r)"echo "OS: $(cat /etc/os-release | grep PRETTY_NAME | cut -d'"' -f2)"echo "Uptime: $(uptime -p)"echo "Load Average: $(cat /proc/loadavg | awk '{print $1, $2, $3}')"echo ""
echo "CPU Info:"echo " Model: $(grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2)"echo " Cores: $(nproc)"echo ""
echo "Memory Info:"free -h | awk '/^Mem:/ {print " Used: " $3 " / " $2}'echo ""
echo "Disk Usage:"df -h | grep -E '^/dev/'echo ""
echo "Network Interfaces:"ip -br addr show | grep UPecho ""
echo "==============================================="echo " Report Complete"echo "==============================================="Running the Script
Section titled “Running the Script”# Make it executablechmod +x system_info.sh
# Run it./system_info.sh
# Or run it directly with bash (doesn't need executable permission)bash system_info.sh
# Run with absolute path/home/user/scripts/system_info.shVariables
Section titled “Variables”Declaring and Using Variables
Section titled “Declaring and Using Variables”Variables in bash are declared by simply assigning a value.
# Basic variable assignment (NO spaces around =)name="John"age=30is_admin=true
# Using variablesecho "Name: $name"echo "Age: $age"
# Alternative syntax (useful in strings)echo "Name: ${name}"Variable Types
Section titled “Variable Types”# Stringname="Alice"echo "String: $name"
# Integercount=42echo "Integer: $count"
# Array (covered in detail later)colors=("red" "green" "blue")echo "Array: ${colors[@]}"
# Associative array (bash 4+)declare -A user_infouser_info["name"]="Bob"user_info["email"]="bob@example.com"echo "Associative: ${user_info[name]}"Basic Commands
Section titled “Basic Commands”echo - Print to Standard Output
Section titled “echo - Print to Standard Output”# Basic usageecho "Hello, World!"
# Without newlineecho -n "No newline: "
# Enable interpretation of escape sequencesecho -e "Line1\nLine2"
# Common escape sequences:# \n - New line# \t - Tab# \r - Carriage return# \b - Backspace# \\ - Backslash
# Examplesecho -e "First line\nSecond line"echo -e "Column1\tColumn2"echo -e "This\b removes character"echo -e "Path: /home/user\\name"printf - Formatted Output
Section titled “printf - Formatted Output”# Basic usageprintf "Hello, World!\n"
# Format specifiersprintf "String: %s\n" "hello"printf "Integer: %d\n" 42printf "Float: %.2f\n" 3.14159
# Multiple argumentsprintf "%s is %d years old\n" "Alice" 30
# Paddingprintf "%10s\n" "hello" # Right alignprintf "%-10s\n" "hello" # Left alignprintf "%05d\n" 42 # Zero-pad
# Field widthprintf "|%10s|%10s|\n" "Name" "Value"printf "|%-10s|%-10s|\n" "Name" "Value"Input/Output Redirection
Section titled “Input/Output Redirection”Understanding File Descriptors
Section titled “Understanding File Descriptors”┌─────────────────────────────────────────────────────────────────────┐│ FILE DESCRIPTORS IN BASH │└─────────────────────────────────────────────────────────────────────┘
┌───────────┬────────────┬─────────────────────────────────────────────┐│ Descriptor│ Name │ Description │├───────────┼────────────┼─────────────────────────────────────────────┤│ 0 │ stdin │ Standard input (keyboard) ││ 1 │ stdout │ Standard output (terminal) ││ 2 │ stderr │ Standard error (terminal) │└───────────┴────────────┴─────────────────────────────────────────────┘
┌───────────────┐ │ Terminal │ └───────┬───────┘ │ ┌───────────────┼───────────────┐ │ │ │ ▼ ▼ ▼ stdin (0) stdout (1) stderr (2) │ │ │ ▼ ▼ ▼ ┌───────┐ ┌───────────┐ ┌───────────┐ │ Input │ │ Output │ │ Error │ │ (read)│ │ (success) │ │ (issues) │ └───────┘ └───────────┘ └───────────┘Redirection Operators
Section titled “Redirection Operators”# Redirect stdout to file (overwrite)echo "Hello" > output.txt
# Redirect stdout to file (append)echo "Hello" >> output.txt
# Redirect stderr to filecommand 2> error.log
# Redirect both stdout and stderr to filecommand &> all_output.logcommand > output.log 2>&1 # Older syntax
# Redirect stderr to stdoutcommand 2>&1 | grep "error"
# Discard output (send to /dev/null)command > /dev/null 2>&1
# Read from filewhile read line; do echo "Line: $line"done < input.txtExamples
Section titled “Examples”# Example 1: Save output to filedate > /tmp/current_time.txtecho "Time saved to file"
# Example 2: Append to log fileecho "$(date): Script started" >> /var/log/myscript.log
# Example 3: Redirect errors onlyfind / -name "file.txt" 2>/dev/null
# Example 4: Custom error loggingcommand_that_fails 2>&1 | tee -a error.log
# Example 5: Read lines from filewhile IFS= read -r line; do echo "Processing: $line"done < "config.txt"Command Execution
Section titled “Command Execution”Running Commands
Section titled “Running Commands”# Simple commandls -la
# Multiple commands on one lineecho "Date:" && dateecho "Uptime:" && uptime
# Run commands sequentiallyecho "Step 1"; echo "Step 2"; echo "Step 3"
# Conditional execution# Run command2 only if command1 succeedscommand1 && command2
# Run command2 only if command1 failscommand1 || command2Command Substitution
Section titled “Command Substitution”# Capture command output into a variablecurrent_date=$(date)echo "Today is: $current_date"
# Older syntax (backticks)current_date=`date`echo "Today is: $current_date"
# Using in placeecho "Hostname: $(hostname)"
# Nested command substitutionecho "Home dir owner: $(ls -ld $HOME | awk '{print $3}')"Exit Status
Section titled “Exit Status”Understanding Exit Codes
Section titled “Understanding Exit Codes”# Every command returns an exit status# 0 = success# 1-255 = error (specific meaning varies by command)
# Check exit status of last commandls -la /tmpecho "Exit status: $?" # $? contains the exit status
# Test exit statusif [ $? -eq 0 ]; then echo "Command succeeded"else echo "Command failed"fi
# Using exit in scriptsexit 0 # Successexit 1 # General errorexit 2 # Misuse of shell commandexit 126 # Command not executableexit 127 # Command not foundexit 128 # Invalid exit argument
# Example: Exit with statusgrep "pattern" file.txtif [ $? -eq 0 ]; then echo "Pattern found"else echo "Pattern not found" exit 1fiQuotes and Escaping
Section titled “Quotes and Escaping”Single vs Double Quotes
Section titled “Single vs Double Quotes”# Double quotes - allow variable expansionname="World"echo "Hello, $name" # Output: Hello, Worldecho "Date: $(date)" # Output: Date: Sat Jan 15 10:30:00...
# Single quotes - literal (no expansion)echo 'Hello, $name' # Output: Hello, $nameecho 'Date: $(date)' # Output: Date: $(date)
# Backticks - command substitution (legacy)echo "Result: `date +%H:%M`"
# Here-stringread -r word <<< "hello world"echo $word # Output: helloEscape Characters
Section titled “Escape Characters”# Escape special charactersecho "Path: /home/user\tname" # \t = tabecho "New\nline" # \n = newlineecho "Price: \$10" # \$ = literal $echo "Backslash: \\" # \\ = literal \
# Using printf for more controlprintf '%s\n' "Line 1" "Line 2"printf '%-10s|%-10s\n' "Left" "Right"Basic Arithmetic
Section titled “Basic Arithmetic”Using let
Section titled “Using let”# Integer arithmetic with letlet a=5+3let b=a*2let "c = (a + b) / 2"
# Shorthand operatorslet a+=5 # Same as: let a=a+5let b-=3 # Same as: let b=b-3let c*=2 # Same as: let c=c*2Using $(( ))
Section titled “Using $(( ))”# Arithmetic expansionresult=$((5 + 3))result=$((10 - 2))result=$((4 * 3))result=$((15 / 4)) # Integer division: 3result=$((15 % 4)) # Modulo: 3
# Using variablesa=10b=3sum=$((a + b))product=$((a * b))Using expr
Section titled “Using expr”# expr command (older method)result=$(expr 5 + 3)result=$(expr 10 - 2)
# Note: spaces are requiredresult=$(expr 5+3) # Wrong! Returns "5+3"result=$(expr 5 + 3) # Correct! Returns "8"Floating Point (bc)
Section titled “Floating Point (bc)”# bc for floating pointresult=$(echo "10 / 3" | bc -l)echo $result # 3.33333...
# Scale (precision)result=$(echo "scale=2; 10 / 3" | bc)echo $result # 3.33
# More complex mathresult=$(echo "sqrt(16) + 2^3" | bc -l)echo $result # 12.00000Working with Strings
Section titled “Working with Strings”String Operations
Section titled “String Operations”# String concatenationstr1="Hello"str2="World"combined="$str1 $str2"echo $combined # Hello World
# String lengthtext="Hello World"length=${#text}echo $length # 11
# Substringtext="Hello World"echo ${text:0:5} # Helloecho ${text:6:5} # World
# String replacementtext="Hello World"echo ${text/World/Universe} # Hello Universeecho ${text//l/L} # HeLLo WorLd (replace all)
# Pattern-based operationsfilename="report-2024-01-15.csv"echo ${filename%.csv} # report-2024-01-15 (remove extension)echo ${filename##*-} # 01-15.csv (remove prefix)Arrays (Basic Introduction)
Section titled “Arrays (Basic Introduction)”Indexed Arrays
Section titled “Indexed Arrays”# Declare arrayfruits=("apple" "banana" "cherry")
# Access elementsecho ${fruits[0]} # appleecho ${fruits[1]} # bananaecho ${fruits[2]} # cherry
# All elementsecho ${fruits[@]} # apple banana cherryecho ${fruits[*]} # apple banana cherry
# Array lengthecho ${#fruits[@]} # 3
# Add elementfruits+="orange"fruits+=(grape)
# Specific element lengthecho ${#fruits[0]} # 5 (length of "apple")Conditional Basics
Section titled “Conditional Basics”The test Command
Section titled “The test Command”# Numeric comparisons[ 5 -eq 5 ] # Equal[ 5 -ne 3 ] # Not equal[ 5 -gt 3 ] # Greater than[ 5 -lt 10 ] # Less than[ 5 -ge 5 ] # Greater or equal[ 3 -le 5 ] # Less or equal
# String comparisons[ "abc" = "abc" ] # Equal[ "abc" != "xyz" ] # Not equal[ -z "" ] # Empty string[ -n "text" ] # Non-empty string
# File tests[ -f "file.txt" ] # Regular file exists[ -d "dir" ] # Directory exists[ -r "file" ] # File is readable[ -w "file" ] # File is writable[ -x "file" ] # File is executable[ -e "file" ] # File exists[ -s "file" ] # File exists and is non-empty
# Combining tests[ -f "file" ] && [ -r "file" ][ -d "dir" ] || [ -d "backup" ]Using [[ (Modern Test)
Section titled “Using [[ (Modern Test)”# [[ is more powerful than [# Supports pattern matching and regex
# Pattern matching[[ "filename.txt" == *.txt ]] # True[[ "hello" == h* ]] # True
# Regex (bash 3+)[[ "email@example.com" =~ ^[a-z]+@[a-z]+\.[a-z]+$ ]]
# No word splitting (safer)text="hello world"[[ $text == "hello world" ]] # Works correctly
# Combined with && and ||[[ -f "file" ]] && echo "File exists"Control Flow Preview
Section titled “Control Flow Preview”If Statement
Section titled “If Statement”# Basic ifif [ condition ]; then echo "Condition is true"fi
# If-elseif [ condition ]; then echo "True"else echo "False"fi
# If-elif-elseif [ $status = "running" ]; then echo "Service is running"elif [ $status = "stopped" ]; then echo "Service is stopped"else echo "Unknown status"fiPractical Examples
Section titled “Practical Examples”Example 1: Check if File Exists
Section titled “Example 1: Check if File Exists”#!/usr/bin/env bash# check_file.sh - Check if a file exists and is readable
FILE="${1:-/etc/passwd}"
if [ -f "$FILE" ]; then echo "File exists: $FILE" if [ -r "$FILE" ]; then echo "File is readable" echo "First 5 lines:" head -5 "$FILE" else echo "File is NOT readable" exit 1 fielse echo "File does not exist: $FILE" exit 1fiExample 2: Simple Calculator
Section titled “Example 2: Simple Calculator”#!/usr/bin/env bash# calculator.sh - Simple arithmetic calculator
if [ $# -ne 3 ]; then echo "Usage: $0 <num1> <operator> <num2>" echo "Operators: + - * / %" exit 1fi
num1=$1operator=$2num2=$3
case $operator in +) result=$((num1 + num2)) ;; -) result=$((num1 - num2)) ;; \*) result=$((num1 * num2)) ;; /) result=$((num1 / num2)) ;; %) result=$((num1 % num2)) ;; *) echo "Invalid operator: $operator" exit 1 ;;esac
echo "$num1 $operator $num2 = $result"Example 3: Backup Script
Section titled “Example 3: Backup Script”#!/usr/bin/env bash# backup.sh - Simple backup script
set -euo pipefail
SOURCE_DIR="/home/user/documents"BACKUP_DIR="/tmp/backups"TIMESTAMP=$(date +%Y%m%d_%H%M%S)BACKUP_NAME="backup_${TIMESTAMP}.tar.gz"
echo "Starting backup..."echo "Source: $SOURCE_DIR"echo "Destination: $BACKUP_DIR/$BACKUP_NAME"
# Create backup directory if it doesn't existmkdir -p "$BACKUP_DIR"
# Create the backuptar -czf "$BACKUP_DIR/$BACKUP_NAME" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")"
# Check if backup was successfulif [ -f "$BACKUP_DIR/$BACKUP_NAME" ]; then size=$(du -h "$BACKUP_DIR/$BACKUP_NAME" | cut -f1) echo "Backup successful! Size: $size" echo "Location: $BACKUP_DIR/$BACKUP_NAME"else echo "Backup failed!" exit 1fiDebugging Scripts
Section titled “Debugging Scripts”Common Issues and Solutions
Section titled “Common Issues and Solutions”# Issue 1: Spaces around = in assignment# Wrong:name = "value" # This tries to run "name" as a command
# Correct:name="value"
# Issue 2: Unquoted variables# Wrong:filename="my file.txt"rm $filename # May fail if filename has spaces
# Correct:rm "$filename"
# Issue 3: Missing command arguments# Wrong:grep pattern # Missing file argument
# Correct:grep pattern file.txt
# Issue 4: Not checking exit status# Wrong:grep "pattern" file.txtecho "Found it" # Runs even if grep fails
# Correct:grep "pattern" file.txt && echo "Found it"Debug Mode
Section titled “Debug Mode”# Run script in debug modebash -x script.sh
# Enable debug within scriptset -x # Start debuggingecho "This will be traced"set +x # Stop debugging
# Trace specific sections#!/bin/bashecho "Normal execution"( set -x echo "Debug this section" ls -la set +x)echo "Back to normal"Summary
Section titled “Summary”In this chapter, you learned:
- ✅ How to structure a bash script properly
- ✅ The importance of the shebang line
- ✅ How to use comments effectively
- ✅ Creating and running your first scripts
- ✅ Working with variables
- ✅ Basic commands: echo and printf
- ✅ Input/output redirection
- ✅ Command execution methods
- ✅ Exit status and why it matters
- ✅ Working with quotes and escaping
- ✅ Basic arithmetic operations
- ✅ String operations
- ✅ Introduction to arrays
- ✅ Conditional testing basics
- ✅ Debugging techniques
Exercises
Section titled “Exercises”Level 1: Practice
Section titled “Level 1: Practice”- Create a script that prints your name and today’s date
- Create a script that calculates the sum of two numbers
- Write a script that checks if a file exists
Level 2: Intermediate
Section titled “Level 2: Intermediate”- Create a script that backs up a directory to /tmp
- Write a script that takes a filename as argument and checks if it’s readable
- Create a simple calculator that handles division
Level 3: Advanced
Section titled “Level 3: Advanced”- Write a script that processes command-line arguments properly
- Create a script with proper error handling and logging
- Implement a simple menu system using case statements
Next Steps
Section titled “Next Steps”Now that you understand basic syntax, continue to the next chapter to learn about variables and parameters in detail.
Previous Chapter: Environment Setup Next Chapter: Variables and Parameters