Skip to content

Parameter_expansion

Parameter expansion is a powerful feature that allows you to manipulate variable values in sophisticated ways without calling external commands. This chapter covers all parameter expansion techniques essential for efficient bash scripting.


Parameter expansion is the mechanism by which the shell replaces a parameter (variable) with its value. It goes far beyond simple $variable substitution.

┌────────────────────────────────────────────────────────────────┐
│ Parameter Expansion Types │
├────────────────────────────────────────────────────────────────┤
│ │
│ Basic: │
│ ────── │
│ $VAR → value │
│ ${VAR} → value │
│ │
│ Default Values: │
│ ───────────── │
│ ${VAR:-value} → value if unset/null │
│ ${VAR:=value} → assign if unset/null │
│ ${VAR:+value} → alt value if set │
│ ${VAR:?message} → error if unset/null │
│ │
│ String Operations: │
│ ─────────────── │
│ ${#VAR} → length │
│ ${VAR:offset} → substring from offset │
│ ${VAR:offset:len} → substring │
│ │
│ Pattern Removal: │
│ ─────────────── │
│ ${VAR#pattern} → remove shortest prefix │
│ ${VAR##pattern} → remove longest prefix │
│ ${VAR%pattern} → remove shortest suffix │
│ ${VAR%%pattern} → remove longest suffix │
│ │
│ Pattern Replacement: │
│ ────────────────── │
│ ${VAR/pattern/string} → replace first │
│ ${VAR//pattern/string} → replace all │
│ ${VAR/#pattern/string} → replace prefix │
│ ${VAR/%pattern/string} → replace suffix │
│ │
└────────────────────────────────────────────────────────────────┘

Terminal window
# Basic variable
name="Alice"
echo $name
echo ${name}
# In strings
echo "Hello, $name"
echo "Hello, ${name}"

Terminal window
# ${parameter:-default}
# Returns default if parameter is unset or null
unset var1
var2=""
echo "${var1:-default}" # default (var1 is unset)
echo "${var2:-default}" # default (var2 is null)
echo "${var3:-default}" # default (var3 never set)
# Common use
echo "${CONFIG_FILE:-/etc/default.conf}"
Terminal window
# ${parameter:=default}
# Assigns default if parameter is unset or null
unset var1
echo "${var1:=assigned}" # assigned, var1 now has value
var2=""
echo "${var2:=assigned}" # assigned, var2 now has value
# Common use
: "${DATABASE_HOST:=localhost}"
# : is a no-op, but expansion happens
Terminal window
# ${parameter:+alternate}
# Returns alternate if parameter is set and non-null
var1="value"
var2=""
echo "${var1:+alternate}" # alternate (var1 is set)
echo "${var2:+alternate}" # empty (var2 is null)
echo "${var3:+alternate}" # empty (var3 is unset)
# Practical use
DEBUG_MODE=1
echo "${DEBUG_MODE:+--debug-flag}" # --debug-flag if set
Terminal window
# ${parameter:?message}
# Errors out if parameter is unset or null
unset important_var
# This will print error and exit
# echo "${important_var:?Error: Variable not set}"
# More specific message
# echo "${important_var:?Please set the important_var variable}"
# Practical use in scripts
: "${DATABASE_URL:?Error: DATABASE_URL must be set}"

Terminal window
# ${#parameter}
name="Hello"
echo ${#name} # 5
path="/home/user/documents"
echo ${#path} # 22
# Array length (first element)
arr=(one two three)
echo ${#arr} # 3 (length of first element)
# Array length (all elements)
echo ${#arr[@]} # 3

Terminal window
# ${parameter:offset}
# ${parameter:offset:length}
text="Hello World"
# From offset (0-based)
echo "${text:6}" # World (from position 6)
# With length
echo "${text:0:5}" # Hello (first 5 chars)
echo "${text:6:5}" # World (5 chars from position 6)
# Negative offset (from end)
echo "${text:-6}" # Hello World (treats -6 as literal with :-)
echo "${text: -5}" # World (space before -5 is required)
echo "${text: -5:2}" # Wo

Terminal window
# ${parameter#pattern} - shortest match
# ${parameter##pattern} - longest match
path="/home/user/documents/file.txt"
# Remove shortest match
echo "${path#*/}" # home/user/documents/file.txt (until /)
# Remove longest match
echo "${path##*/}" # file.txt (last /)
# With patterns
filename="/var/log/nginx/access.log"
echo "${filename##*/}" # access.log
Terminal window
# ${parameter%pattern} - shortest match
# ${parameter%%pattern} - longest match
path="/home/user/documents/file.txt"
# Remove shortest match
echo "${path%/*}" # /home/user/documents (after last /)
# Remove longest match
echo "${path%%/*}" # (first /)
# Extension removal
filename="document.tar.gz"
echo "${filename%.*}" # document.tar
echo "${filename%%.*}" # document

Terminal window
# ${parameter/pattern/string}
text="hello world hello"
# Replace first occurrence
echo "${text/hello/bye}" # bye world hello
# Case insensitive
echo "${text//[Hh]ello/bye}" # bye world bye
# If string is empty
echo "${text/pattern/}" # Removes first match
echo "${text//pattern/}" # Removes all matches
Terminal window
# ${parameter//pattern/string}
text="hello world hello"
echo "${text//hello/bye}" # bye world bye
echo "${text//l/_}" # he__o wor__d he__o
Terminal window
# ${parameter/#pattern/string}
filename="prefix_file.txt"
echo "${filename/#prefix_/}" # file.txt
url="https://example.com"
echo "${url#https://}" # example.com
Terminal window
# ${parameter/%pattern/string}
filename="document.txt"
echo "${filename%.txt}" # document
url="example.com/index.html"
echo "${url%.html}" # example.com/index

Terminal window
# Array
arr=(one two three four)
echo ${#arr} # 3 (length of first element)
echo ${#arr[@]} # 4 (number of elements)
echo ${#arr[*]} # 4
Terminal window
arr=(a b c d e f)
# All elements from index
echo ${arr[@]:2} # c d e f
# Elements from index with count
echo ${arr[@]:2:2} # c d
Terminal window
arr=(one two three two four)
# Remove first matching element
unset 'arr[1]' # Remove element at index 1
arr=( "${arr[@]/two}" ) # Remove by value
# Remove all matching
arr=( "${arr[@]//two}" )

#!/bin/bash
# $0, $1, $2, etc.
echo $0 # Script name
echo $1 # First argument
echo $2 # Second argument
echo $# # Number of arguments
echo $@ # All arguments
echo $* # All arguments (single string)
# Shift
shift # $2 becomes $1
shift 2 # Shift by 2
Terminal window
echo $$ # Current PID
echo $! # PID of last background job
echo $? # Exit status of last command
echo $! # PID of last background process

#!/usr/bin/env bash
# Get filename from path
filepath="/var/log/nginx/access.log"
filename="${filepath##*/}" # access.log
# Get directory from path
dir="${filepath%/*}" # /var/log/nginx
# Get extension
ext="${filename##*.}" # log
# Base name without extension
base="${filename%.*}" # access
#!/usr/bin/env bash
# Application configuration with sensible defaults
export APP_NAME="${APP_NAME:-myapp}"
export APP_ENV="${APP_ENV:-production}"
export APP_PORT="${APP_PORT:-8080}"
export LOG_LEVEL="${LOG_LEVEL:-info}"
export MAX_CONNECTIONS="${MAX_CONNECTIONS:-100}"
export TIMEOUT_SECONDS="${TIMEOUT_SECONDS:-30}"
echo "Starting $APP_NAME in $APP_ENV mode"
echo "Port: $APP_PORT, Log: $LOG_LEVEL"
#!/usr/bin/env bash
validate_required() {
local var_name="$1"
local var_value="${!var_name}" # Indirect expansion
if [[ -z "$var_value" ]]; then
echo "Error: $var_name is required but not set"
return 1
fi
}
# Usage
DATABASE_URL="postgres://localhost/db"
validate_required "DATABASE_URL"
#!/usr/bin/env bash
# Convert to lowercase
text="HELLO WORLD"
echo "${text,,}" # hello world
# Convert to uppercase
text="hello world"
echo "${text^^}" # HELLO WORLD
# Replace patterns
config="DEBUG=true"
echo "${config,,}" # debug=true
# Remove quotes
value='"quoted string"'
echo "${value//\"}" # quoted string
#!/usr/bin/env bash
email="john.doe@example.com"
# Get username (before @)
username="${email%%@*}" # john.doe
# Get domain (after @)
domain="${email#*@}" # example.com

Terminal window
var1="value1"
var_name="var1"
# Get value by name
echo "${!var_name}" # value1
# For arrays
arr=(one two three)
i=0
echo "${!arr[$i]}" # one (element at index 0)
Terminal window
# Bash 4+
text="Hello World"
# Lowercase
echo "${text,,}" # hello world
# Uppercase
echo "${text^^}" # HELLO WORLD
# Lowercase first char
echo "${text,}" # hello World
# Uppercase first char
echo "${text^}" # Hello World
#!/usr/bin/env bash
# Parse Kubernetes resource name
resource="deployment/myapp-5d9f7b8-x"
name="${resource##*/}" # myapp-5d9f7b8-x
deployment="${resource%%/*}" # deployment
# Docker image tag
image="registry.example.com/app:v1.2.3"
registry="${image%%/*}" # registry.example.com/app
tag="${image##*:}" # v1.2.3
name="${image%:*}" # registry.example.com/app
# Semver comparison
version="v1.2.3"
major="${version%%.*}" # v1
minor="${version#*.}" # 2.3
minor="${minor%%.*}" # 2
patch="${version##*.}" # 3

In this chapter, you learned:

  • ✅ Basic parameter expansion
  • ✅ Default value operators (:-), (:=), (:+), (:?)
  • ✅ String length with # operator
  • ✅ Substring extraction
  • ✅ Pattern removal (#, ##, %, %%)
  • ✅ Pattern replacement (/, //, #/, %/)
  • ✅ Arrays and parameter expansion
  • ✅ Special parameters (positional, $, ?, !)
  • ✅ Practical DevOps examples
  • ✅ Advanced techniques (indirect expansion, case modification)

Continue to the next chapter to learn about Command Substitution.


Previous Chapter: Shell Expansion Next Chapter: Command Substitution