5.3. Parameter Substitution#

Parameter substitution is bash’s way of doing sophisticated variable operations without calling external programs. It uses special syntax ${var...} to provide defaults, check if variables exist, extract parts of strings, and more.

This is where bash programming becomes powerful—you can write complex logic entirely with parameter expansion.

5.3.1. Common Pitfalls#

5.3.1.1. ❌ Forgetting Braces#

# WRONG
$ echo $var#pattern  # Just prints variable, ignores #pattern

# RIGHT
$ echo ${var#pattern}

5.3.1.2. ❌ Pattern Syntax#

$ filename="document.txt"

# WRONG: Literal dot
$ echo ${filename%.txt}
document   # Matched "txt" not ".txt"

# RIGHT: Escape the dot or use different pattern
$ echo ${filename#*.}
txt

# Or be more specific
$ base="${filename%.*}"

5.3.1.3. ❌ Substring With Wrong Syntax#

# WRONG
$ echo $text[0:3]

# RIGHT
$ echo ${text:0:3}

5.3.2. When to Use Parameter Substitution#

Use when:

  • You need defaults for optional parameters

  • You’re extracting parts of filenames/paths

  • You need string manipulation without external commands

  • You want error messages for missing variables

Don’t use when:

  • The operation is complex (use sed/awk instead)

  • You need regex (grep is better)

  • It makes code hard to read

# ✓ Good use: simple default
filename="${1:-default.txt}"

# ✗ Overuse: confusing nesting
result="${${var#prefix}%suffix}"

# Better:
temp="${var#prefix}"
result="${temp%suffix}"

5.3.3. All Parameter Substitution Patterns#

Syntax

Meaning

${var:-word}

Default if unset

${var:=word}

Default if unset, and assign

${var:?message}

Error if unset

${var:+word}

Substitute if set

${#var}

Length of value

${var#pattern}

Remove shortest prefix

${var##pattern}

Remove longest prefix

${var%pattern}

Remove shortest suffix

${var%%pattern}

Remove longest suffix

${var/old/new}

Replace first

${var//old/new}

Replace all

${var/#old/new}

Replace at start

${var/%old/new}

Replace at end

${var:offset:length}

Extract substring

${var^^}

Convert to uppercase

${var,,}

Convert to lowercase

5.3.4. Complex Examples#

5.3.4.1. Parse URL#

url="https://user:pass@example.com:8080/path"

# Extract protocol
protocol="${url%%://*}"  # https

# Remove protocol
rest="${url#*://}"       # user:pass@example.com:8080/path

# Extract host (remove user info)
host="${rest#*@}"        # example.com:8080/path

# Extract just domain
domain="${host%%:*}"     # example.com

# Extract path
path="${rest#*/}"        # path

5.3.4.2. Generate Backup Filename#

filename="data.csv"
timestamp=$(date +%Y%m%d_%H%M%S)

# Remove extension, add timestamp and restore extension
backup="${filename%.*}.${timestamp}.${filename##*.}"
echo $backup
# Output: data.20250115_103045.csv

5.3.4.3. Validate and Set Defaults#

#!/bin/bash

# Command line arguments with defaults
input_file="${1:?Input file required}"
output_file="${2:-output.txt}"
format="${3:-json}"

echo "Input: $input_file"
echo "Output: $output_file"
echo "Format: $format"

5.3.5. String Conversion#

Convert to uppercase or lowercase (bash 4+):

$ name="Alice"

# Convert to uppercase
$ echo ${name^^}
ALICE

# Convert to lowercase
$ echo ${name,,}
alice

# Capitalize first letter
$ echo ${name^}
Alice

# Lowercase first letter
$ echo ${name,}
alice

5.3.6. Extract Substrings#

Get a portion of a string:

# Extract 3 characters starting at position 0
$ text="hello"
$ echo ${text:0:3}
hel

# Extract starting at position 1
$ echo ${text:1}
ello

# Extract 2 characters starting at position 1
$ echo ${text:1:2}
el

# Negative offsets (from end)
$ echo ${text: -3}
llo
# From 3 characters before the end

5.3.7. String Length#

Get the length of a variable:

$ password="secret"
$ echo ${#password}
6

$ path="/home/alice/documents/file.txt"
$ echo ${#path}
30

# Useful for validation
if [ ${#password} -lt 8 ]; then
    echo "Password too short"
fi

5.3.8. Finding and Replacing#

Replace parts of a string:

5.3.8.1. Replace First Match#

$ text="the cat in the hat"
$ echo ${text/the/a}
a cat in the hat
# Only first "the" replaced

$ text="foo.old.old"
$ echo ${text/old/new}
foo.new.old
# Only first "old" replaced

5.3.8.2. Replace All Matches#

$ text="the cat in the hat"
$ echo ${text//the/a}
a cat in a hat
# All "the" replaced

$ text="foo-bar-baz"
$ echo ${text///-/_}
foo_bar_baz
# All hyphens become underscores

5.3.8.3. Replace at Start or End#

$ text="test123"

# Replace at start (if match)
$ echo ${text/#test/exam}
exam123

# Replace at end (if match)
$ echo ${text/%123/456}
test456

# No match, returns original
$ echo ${text/#foo/bar}
test123

5.3.9. Removing Prefixes and Suffixes#

Remove parts of a string:

5.3.9.1. Remove Prefix#

# Remove shortest matching prefix
$ filename="document.txt"
$ echo ${filename#*.}
txt
# Removes everything up to and including first "."

# Remove longest matching prefix
$ path="/home/alice/documents/file.txt"
$ echo ${path##*/}
file.txt
# Removes everything up to and including last "/"

$ echo ${path#*/}
home/alice/documents/file.txt
# Just first "/" removed

5.3.9.2. Remove Suffix#

# Remove shortest matching suffix
$ filename="document.txt"
$ echo ${filename%.*}
document
# Removes from last "." to end

# Remove longest matching suffix
$ filename="archive.tar.gz"
$ echo ${filename%%.*}
archive
# Removes from first "." to end

$ echo ${filename%.*}
archive.tar
# Just last ".gz" removed

5.3.9.3. Practical Examples#

# Extract filename from path
$ path="/home/alice/documents/file.txt"
$ filename="${path##*/}"
$ echo $filename
file.txt

# Remove extension
$ extension="${filename#*.}"
$ base="${filename%.*}"
$ echo "Base: $base, Extension: $extension"
Base: file, Extension: txt

# Remove directory from path
$ dir="${path%/*}"
$ echo $dir
/home/alice/documents

5.3.10. Default Values#

One of the most useful operations: provide a default if variable is unset or empty.

5.3.10.1. Use Default If Unset#

$ echo ${unset_var:-default}
default
# unset_var doesn't exist, uses "default"

$ var="actual"
$ echo ${var:-default}
actual
# var is set, uses its value

5.3.10.2. Use Default If Empty#

$ var=""
$ echo ${var:-default}
# (empty, var is set even if empty)

$ echo ${var:=default}
default
# Sets var to default and echoes it

# Now var is set
$ echo $var
default

5.3.10.3. Require Variable (Error If Unset)#

$ echo ${var:?Variable must be set}
# If var is unset, displays error and exits

# Useful for required parameters
$ echo ${api_key:?API_KEY not configured}

5.3.11. Basic Syntax#

Parameter substitution uses the brace syntax:

$ var="value"
$ echo $var              # Simple expansion
value

$ echo ${var}            # Explicit (same as above)
value

$ echo ${var#...}        # Remove prefix
$ echo ${var%...}        # Remove suffix
$ echo ${var/find/repl}  # Find and replace

The braces {} are required for these operations.