Skip to main content

Basics

Variables

Declaration and Assignment

# Variable assignment (no spaces around =)
name="John"
age=25
readonly PI=3.14159

# Command substitution
current_date=$(date)
files=$(ls *.txt)

# Arithmetic expansion
result=$((5 + 3))
count=$((count + 1))

Variable Expansion

# Basic expansion
echo $name
echo ${name}

# Default values
echo ${name:-"default"} # Use default if name is unset
echo ${name:="default"} # Set and use default if name is unset
echo ${name:+"alternative"} # Use alternative if name is set

# String operations
echo ${name#pattern} # Remove shortest match from beginning
echo ${name##pattern} # Remove longest match from beginning
echo ${name%pattern} # Remove shortest match from end
echo ${name%%pattern} # Remove longest match from end

Special Variables

$0      # Script name
$1-$9 # Positional parameters
${10} # Parameters beyond $9
$# # Number of arguments
$@ # All arguments as separate words
$* # All arguments as single word
$? # Exit status of last command
$$ # Process ID of current shell
$! # Process ID of last background job

Data Types

Strings

# Single quotes (literal)
echo 'Hello $name' # Outputs: Hello $name

# Double quotes (variable expansion)
echo "Hello $name" # Outputs: Hello John

# Concatenation
full_name="$first_name $last_name"
path="/home/$USER/documents"

# String length
echo ${#string}

# Substring extraction
echo ${string:position}
echo ${string:position:length}

Numbers

# Integer arithmetic
result=$((5 + 3))
result=$((a * b))
result=$((a / b))
result=$((a % b))

# Increment/decrement
((count++))
((count--))
((count += 5))

# Comparison
if ((a > b)); then
echo "a is greater"
fi

Arrays

# Declaration
declare -a fruits
fruits=("apple" "banana" "cherry")

# Access elements
echo ${fruits[0]} # First element
echo ${fruits[@]} # All elements
echo ${fruits[*]} # All elements (different quoting behavior)

# Array operations
fruits+=("date") # Append element
echo ${#fruits[@]} # Array length
echo ${!fruits[@]} # Array indices

# Associative arrays
declare -A colors
colors["red"]="#FF0000"
colors["green"]="#00FF00"
echo ${colors["red"]}

Operators

Arithmetic Operators

$((a + b))    # Addition
$((a - b)) # Subtraction
$((a * b)) # Multiplication
$((a / b)) # Division
$((a % b)) # Modulo
$((a ** b)) # Exponentiation

Comparison Operators

# Numeric comparison
-eq # Equal
-ne # Not equal
-lt # Less than
-le # Less than or equal
-gt # Greater than
-ge # Greater than or equal

# String comparison
= # Equal
!= # Not equal
< # Less than (lexicographical)
> # Greater than (lexicographical)
-z # Empty string
-n # Non-empty string

# Examples
if [ $a -eq $b ]; then echo "Equal"; fi
if [ "$str1" = "$str2" ]; then echo "Same"; fi
if [ -z "$var" ]; then echo "Empty"; fi

File Test Operators

-f file    # Regular file exists
-d file # Directory exists
-e file # File exists (any type)
-r file # File is readable
-w file # File is writable
-x file # File is executable
-s file # File is not empty
-L file # File is symbolic link

# Examples
if [ -f "config.txt" ]; then
echo "Config file exists"
fi

if [ -d "/tmp" ]; then
echo "Directory exists"
fi

Logical Operators

&&     # AND
|| # OR
! # NOT

# Examples
[ $a -gt 0 ] && [ $a -lt 10 ] # a is between 0 and 10
[ $a -eq 0 ] || [ $a -eq 1 ] # a is 0 or 1
[ ! -f "file.txt" ] # file.txt does not exist

Input/Output

Reading Input

# Read from user
read name
echo "Hello $name"

# Read with prompt
read -p "Enter your age: " age

# Read password (hidden)
read -s -p "Password: " password

# Read with timeout
read -t 10 -p "Enter choice (10s timeout): " choice

# Read into array
read -a words <<< "word1 word2 word3"

Output

# Print to stdout
echo "Hello World"
printf "Name: %s, Age: %d\n" "$name" $age

# Print to stderr
echo "Error message" >&2

# Redirect output
echo "Hello" > file.txt # Overwrite
echo "World" >> file.txt # Append
command 2> error.log # Redirect stderr
command &> output.log # Redirect both stdout and stderr

Command Line Arguments

Processing Arguments

#!/bin/bash

# Basic argument access
echo "Script name: $0"
echo "First argument: $1"
echo "Number of arguments: $#"

# Loop through arguments
for arg in "$@"; do
echo "Argument: $arg"
done

# Shift arguments
shift # Remove $1, shift all arguments left
echo "New first argument: $1"

Getopts for Option Parsing

#!/bin/bash

while getopts "hvf:" opt; do
case $opt in
h)
echo "Usage: $0 [-h] [-v] [-f file]"
exit 0
;;
v)
verbose=true
;;
f)
filename="$OPTARG"
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
esac
done

# Remove processed options
shift $((OPTIND-1))

# Remaining arguments are in $@
echo "Remaining args: $@"

Environment Variables

Common Environment Variables

$HOME      # User's home directory
$PATH # Executable search path
$USER # Current username
$SHELL # Current shell
$PWD # Current working directory
$OLDPWD # Previous working directory
$RANDOM # Random number (0-32767)

Setting Environment Variables

# Set for current session
export MY_VAR="value"

# Set for single command
MY_VAR="value" command

# Unset variable
unset MY_VAR

# Make variable read-only
readonly MY_VAR="value"

Basic Script Structure

Shebang and Script Header

#!/bin/bash

# Script description
# Author: Your Name
# Date: $(date)
# Version: 1.0

set -euo pipefail # Exit on error, undefined vars, pipe failures

# Script content here

Exit Codes

# Successful exit
exit 0

# Error exit
exit 1

# Custom exit codes
exit 42

# Check exit status
if command; then
echo "Success"
else
echo "Failed with exit code $?"
fi

Best Practices

Quoting

# Always quote variables
echo "$name" # Good
echo $name # Bad (can break with spaces)

# Quote command substitution
files="$(ls)" # Good
files=$(ls) # Bad

# Use arrays for multiple values
files=("file1" "file2" "file with spaces")

Error Handling

# Check if command exists
if command -v git >/dev/null 2>&1; then
echo "Git is installed"
else
echo "Git is not installed"
exit 1
fi

# Check file operations
if ! cp source dest; then
echo "Copy failed"
exit 1
fi

Performance Tips

# Use built-in commands when possible
[[ condition ]] # Faster than [ condition ]
${var##*/} # Faster than $(basename "$var")
${var%/*} # Faster than $(dirname "$var")

# Avoid unnecessary subshells
while read line; do # Good
echo "$line"
done < file

cat file | while read line; do # Bad (unnecessary cat)
echo "$line"
done