Bash String Expansion Versus External Commands
Bash string expansion is the shell's builtin way to change a string without starting an external command. It replaces calls to tools like sed, cut, and tr for common edits.
Expansion uppercases with no external call
The expansion and the tr pipeline print the same word. The counter shows only the tr run started an external command.
: > calls.txt
tr() {
echo hit >> calls.txt
command tr "$@"
}
name="hello"
echo "${name^^}"
printf '%s\n' "$name" | tr 'a-z' 'A-Z'
echo "external calls: $(wc -l < calls.txt)"Strip a directory and an extension without cut or sed
The first expansion drops everything up to the last slash. The second drops the trailing extension.
path="/docs/report.txt"
echo "${path##*/}"
echo "${path%.txt}"Replace and slice without sed or awk
One expansion swaps every space for an underscore. Another takes the first five characters.
text="hello world test"
echo "${text// /_}"
echo "${text:0:5}"Example
: > calls.txt
tr() {
echo hit >> calls.txt
command tr "$@"
}
sed() {
echo hit >> calls.txt
command sed "$@"
}
for path in "docs/my report.txt" "docs/todo list.txt"; do
base="${path##*/}"
echo "${base%.txt}"
done
echo "external calls: $(wc -l < calls.txt)"Key points
- Case change: ${name^^} uppercases a value and ${name,,} lowercases it.
- Strip prefix: ${path##*/} drops everything up to the last slash.
- Strip suffix: ${file%.txt} drops a trailing extension.
- Replace all: ${text// /_} swaps every space for an underscore.
- Substring: ${text:0:5} takes the first five characters.
- Zero forks: expansion runs inside the shell, so a loop over many values pays no process startups.
In this example, the loop walks two file paths. For each path, one expansion drops the directory and another drops the extension. No external command runs, so the counter stays at 0.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
Which expansion drops the directory from /docs/report.txt?