Every fork costs about a millisecond. In a loop over ten thousand filenames, replacing basename with parameter expansion is the difference between a script that finishes and one you cancel. More importantly, the expansion is easier to read once you know it.
1. Trimming
path=/var/log/nginx/access.log
echo "${path##*/}" # access.log — longest match from the front
echo "${path%/*}" # /var/log/nginx — shortest match from the back
echo "${path##*.}" # log
echo "${path%.*}" # /var/log/nginx/access
One # or % is a short match, two is greedy. The symbols sit either side of the dollar sign on a keyboard, which is the mnemonic that finally made it stick for me.
2. Defaults and assertions
: "${PORT:=8080}" # assign if unset or empty
: "${TOKEN:?is required}" # abort with a message
echo "${DEBUG:-off}" # use a fallback without assigning
3. Substitution and case
name=deploy-prod-api
echo "${name//-/_}" # deploy_prod_api — replace all
echo "${name/-/_}" # deploy_prod-api — replace first
echo "${name^^}" # DEPLOY-PROD-API
echo "${#name}" # 15
4. Where it stops
Parameter expansion has no regular expressions, only globs. The moment you need a capture group, reach for [[ $var =~ re ]] and read BASH_REMATCH, or accept the fork and use sed. Bending a glob into doing a regex’s job produces the kind of line that nobody, including its author, can modify six months later.