Skip to content
AgileLogics
6 min read

Advanced Expansions and Shell Options

Prerequisites: String manipulation, Arrays

Bash has shell options (shopt) and expansions that make file handling safer and scripts more flexible.

nullglob: unmatched patterns expand to nothing

for f in *.pdf; do echo "processing $f"; done
echo "---"
shopt -s nullglob
for f in *.pdf; do echo "processing $f"; done
echo "no PDFs, so the loop didn't run"
processing *.pdf
---
no PDFs, so the loop didn't run

Without nullglob, the loop runs once with the literal text *.pdf.

globstar: ** matches folders recursively

shopt -s globstar
mkdir -p src/a/b
touch src/main.sh src/a/util.sh src/a/b/deep.sh src/readme.md
for f in src/**/*.sh; do echo "$f"; done
src/a/b/deep.sh
src/a/util.sh
src/main.sh

extglob: richer patterns

shopt -s extglob
touch app.js app.min.js style.css style.min.css index.html
echo !(*.min.*)
echo *.@(js|css)
app.js index.html style.css
app.js app.min.js style.css style.min.css
Pattern Matches
!(pattern) anything except pattern
@(a|b) exactly one of the patterns
+(pattern) one or more
?(pattern) zero or one

Indirect references and namerefs

color_primary="blue"
key="color_primary"
echo "${!key}"

set_default() {
  local -n ref=$1
  ref=${ref:-"$2"}
}
unset timeout
set_default timeout 30
echo "timeout=$timeout"
blue
timeout=30
  • ${!key} expands the variable named by key.
  • local -n ref=$1 makes ref a nameref — another name for the variable whose name was passed in, so the function can change the caller's variable (Bash 4.3+).

Transformations (Bash 4.4+)

name="ada lovelace"
echo "${name@u}"
value="it's done"
echo "${value@Q}"
Ada lovelace
'it'\''s done'

${var@u} capitalizes the first letter (Bash 5.1+); ${var@Q} quotes a value so it can be safely reused as shell input.

Common mistakes

  • Turning on nullglob globally in scripts that pass patterns to other commands (ls *.log with no matches becomes ls, listing everything). Enable it only around the loops that need it.