2026-03-27cli[intermediate][cheatsheet]

Shell Parameter Expansion: Beyond ${var}

Bash parameter expansion can replace most uses of sed, awk, and cut for string manipulation. Learn the syntax once, use it everywhere.

Every time you write $(echo "$var" | sed 's/foo/bar/') in a shell script, there's probably a parameter expansion that does it faster and without spawning a subprocess.

Default Values

${var:-default}    # Use default if var is unset or empty
${var:=default}    # Assign default if var is unset or empty
${var:+alternate}  # Use alternate if var IS set and non-empty
${var:?error msg}  # Exit with error if var is unset or empty

The colon matters. Without it (${var-default}), only checks if unset, not empty.

String Manipulation

${var#pattern}     # Remove shortest match from beginning
${var##pattern}    # Remove longest match from beginning
${var%pattern}     # Remove shortest match from end
${var%%pattern}    # Remove longest match from end

Real Examples

filepath="/home/user/documents/report.tar.gz"

${filepath##*/}      # report.tar.gz  (basename)
${filepath%/*}       # /home/user/documents  (dirname)

filename="${filepath##*/}"
${filename%%.*}      # report  (name without any extension)
${filename%.*}       # report.tar  (name without last extension)
${filename##*.}      # gz  (last extension only)

Substitution

${var/pattern/replacement}   # Replace first match
${var//pattern/replacement}  # Replace all matches
${var/#pattern/replacement}  # Replace if at beginning
${var/%pattern/replacement}  # Replace if at end

Length and Slicing

${#var}              # String length
${var:offset}        # Substring from offset
${var:offset:length} # Substring with length

Case Conversion (Bash 4+)

${var^^}    # UPPERCASE
${var,,}    # lowercase
${var^}     # Capitalize first letter

Pro Tip

Combine expansions for powerful one-liners. Convert a filename from snake_case to kebab-case without any external tools:

file="my_cool_script.sh"
echo "${file//_/-}"   # my-cool-script.sh

Or strip a common prefix from all files in a directory:

prefix="project_v2_"
for f in ${prefix}*; do
  mv "$f" "${f#$prefix}"
done

Example

A practical script that processes log file paths — extracting date, service name, and rotating based on age — using nothing but parameter expansion:

#!/bin/bash
for logfile in /var/log/services/*.2026-*.log; do
  base="${logfile##*/}"             # api-gateway.2026-03-15.log
  service="${base%%.*}"            # api-gateway
  date_part="${base#*.}"           # 2026-03-15.log
  date_part="${date_part%.*}"      # 2026-03-15

  echo "Service: $service, Date: $date_part"
done

No awk. No sed. No cut. Just the shell doing what it was designed to do.