Linux · Bash · Shell Scripting · Automation

Bash Scripting
Cheatsheet

A practical reference for Bash shell scripting — covering variables, loops, conditionals, functions, arrays, string operations, and real-world script patterns.

Language: Bash (sh-compatible)
Extension: .sh
Level: Beginner → Advanced
Sections: 10
DevHints Bash Guide

What is Bash?

Bash (Bourne Again SHell) is the default command-line interpreter on most Linux distributions and macOS. It is both an interactive shell (for running commands) and a scripting language (for writing automation scripts saved in .sh files).

Why use it? Bash scripts automate repetitive tasks, system administration, deployments, log parsing, backups, and CI/CD pipelines. A single Bash script can replace hours of manual work.

Real-world use: DevOps engineers, sysadmins, security analysts, and developers use Bash daily. It is core to Linux system administration, Ansible playbooks, Docker entrypoints, and GitHub Actions workflows.

Always start scripts with the shebang line: #!/bin/bash This tells the OS to use /bin/bash to interpret the script. Save with .sh extension and make executable: chmod +x script.sh

1. Getting Started

#!/bin/bash
# This is a comment

# Print output
echo "Hello, World!"
echo "Today: $(date)"     # command substitution

# Exit codes
exit 0                  # 0 = success
exit 1                  # non-zero = error
echo $?               # print last exit code

# Make script executable and run
# chmod +x script.sh && ./script.sh

2. Variables

# Assign (no spaces around =)
NAME="Kenneth"
AGE=25
DIR="/var/www"

# Use variable
echo "Hello, $NAME"
echo "Age: ${AGE}"     # braces for clarity

# Command substitution
HOSTNAME=$(hostname)
FILES=$(ls /etc | wc -l)

# Read-only variable
readonly PI=3.14159

# Unset variable
unset NAME

# Special variables
echo $0       # script name
echo $1       # first argument
echo $#       # number of arguments
echo $@       # all arguments
echo $$       # current PID
echo $?       # last exit code

3. User Input

# Read from user
read -p "Enter your name: " NAME
echo "Hello, $NAME!"

# Silent input (for passwords)
read -sp "Password: " PASS
echo           # newline after hidden input

# Read with timeout
read -t 10 -p "Input (10s): " INPUT

# Command-line arguments
#!/bin/bash
echo "Script: $0"
echo "Arg 1: $1"
echo "Arg 2: $2"
echo "All args: $@"
# Run: ./script.sh hello world

4. Conditionals

if / elif / else

if [ $AGE -ge 18 ]; then
  echo "Adult"
elif [ $AGE -ge 13 ]; then
  echo "Teen"
else
  echo "Child"
fi

# String comparison
if [ "$NAME" == "root" ]; then
  echo "Root user"
fi

Test operators

# Numeric
-eq  equal to
-ne  not equal
-lt  less than
-le  less or equal
-gt  greater than
-ge  greater or equal

# String
==   equal
!=   not equal
-z   empty string
-n   not empty

# File tests
-f   is a file
-d   is a directory
-e   exists
-r   readable
-x   executable
# File tests in practice
if [ -f "/etc/passwd" ]; then
  echo "File exists"
fi

if [ ! -d "/tmp/mydir" ]; then
  mkdir -p /tmp/mydir
fi

# Compound conditions
if [[ $AGE -gt 18 && "$NAME" != "" ]]; then
  echo "Valid adult"
fi

# case statement
case $OS in
  Ubuntu)  echo "Debian-based" ;;
  CentOS)  echo "RPM-based" ;;
  *)       echo "Unknown" ;;
esac

5. Loops

# for loop — iterate list
for i in 1 2 3 4 5; do
  echo "Number: $i"
done

# for loop — range
for i in {1..10}; do
  echo $i
done

# for loop — C-style
for (( i=0; i<5; i++ )); do
  echo "i=$i"
done

# for loop — iterate files
for file in /var/log/*.log; do
  echo "Processing: $file"
done

# while loop
COUNT=0
while [ $COUNT -lt 5 ]; do
  echo "Count: $COUNT"
  COUNT=$((COUNT + 1))
done

# Read file line by line
while IFS= read -r line; do
  echo "$line"
done < /etc/hosts

# until loop
until [ $COUNT -ge 10 ]; do
  COUNT=$((COUNT + 1))
done

# Loop control
break     # exit loop
continue  # skip to next iteration

6. Functions

# Define a function
greet() {
  local NAME=$1         # local variable (scoped)
  echo "Hello, $NAME!"
}

# Call the function
greet "Kenneth"

# Function with return value
add() {
  local RESULT=$(( $1 + $2 ))
  echo $RESULT         # return via stdout
}
SUM=$(add 5 3)
echo "Sum: $SUM"

# Function with exit code
check_root() {
  if [ $EUID -ne 0 ]; then
    echo "Must be root" >&2
    return 1
  fi
}

check_root || exit 1   # exit if not root

7. Arrays

# Declare array
FRUITS=("apple" "banana" "cherry")
NUMBERS=(1 2 3 4 5)

# Access elements
echo ${FRUITS[0]}      # apple
echo ${FRUITS[1]}      # banana
echo ${FRUITS[-1]}     # last element (cherry)

# All elements
echo ${FRUITS[@]}      # all elements
echo ${#FRUITS[@]}     # array length

# Loop over array
for fruit in ${FRUITS[@]}; do
  echo $fruit
done

# Add / remove elements
FRUITS+=("date")        # append
unset FRUITS[0]        # remove index 0

# Associative arrays (dictionaries)
declare -A PORTS
PORTS["HTTP"]=80
PORTS["HTTPS"]=443
PORTS["SSH"]=22
echo ${PORTS["HTTPS"]}  # 443

8. String Operations

STR="Hello World"

# Length
echo ${#STR}             # 11

# Uppercase / lowercase
echo ${STR^^}            # HELLO WORLD
echo ${STR,,}            # hello world

# Substring
echo ${STR:0:5}          # Hello (pos 0, length 5)
echo ${STR:6}            # World (from pos 6)

# Replace
echo ${STR/World/Bash}   # Hello Bash (first)
echo ${STR//l/L}         # HeLLo WorLd (all)

# Strip prefix / suffix
FILE="report.txt"
echo ${FILE%.txt}         # report (strip suffix)
echo ${FILE#report}       # .txt (strip prefix)

# Default values
echo ${VAR:-"default"}  # use default if VAR unset
echo ${VAR:="default"}  # set and use default if unset

9. Arithmetic

# Arithmetic expansion
RESULT=$(( 5 + 3 ))         # 8
RESULT=$(( 10 / 2 ))        # 5
RESULT=$(( 7 % 3 ))         # 1 (modulus)
RESULT=$(( 2 ** 8 ))        # 256 (power)

# Increment / decrement
COUNT=0
(( COUNT++ ))               # post-increment
(( COUNT += 5 ))            # add 5
COUNT=$(( COUNT - 1 ))      # subtract 1

# Float arithmetic (use bc)
echo "scale=2; 10/3" | bc   # 3.33
AREA=$(echo "scale=2; 3.14 * 5 * 5" | bc)

10. Real-World Script Patterns

#!/bin/bash
# Robust script template
set -euo pipefail          # exit on error, unset var, pipe fail
IFS=$'\n\t'               # safer word splitting

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

# Error handler
die() { echo "ERROR: $*" >&2; exit 1; }

# Check dependency
require() {
  command -v "$1" >/dev/null || die "$1 not found"
}
require curl
require jq

# Trap cleanup on exit
cleanup() {
  log "Cleaning up..."
  rm -f /tmp/tmpfile.$$
}
trap cleanup EXIT

log "Starting script..."

📚 Further Learning