|

Linux CLI 65 🐧 Parameter expansion in shell scripts

⏱️ Estimated read: 15 min | |

#!/bin/bash

var="hello world"

echo "Substring from position 3: ${var:2}"
echo "Substring from position 3 to length 5: ${var:2:5}"

default_var=${unset_var:-"default_value"}
echo "Default value assigned: $default_var"

replace_var=${var/world/universe}
echo "After replacing 'world' with 'universe': $replace_var"

length=${#var}
echo "Length of the variable: $length"

pattern_replace_var=${var//o/O}
echo "After replacing all 'o' with 'O': $pattern_replace_var"

remove_prefix=${var#hello }
echo "Remove shortest matching prefix 'hello ': $remove_prefix"
remove_suffix=${var% world}
echo "Remove shortest matching suffix ' world': $remove_suffix"

Parameter expansion is what turns bash variables from simple placeholders into a real text-processing toolkit. With a single ${...} expression you can extract substrings, provide defaults, replace patterns, strip prefixes and suffixes, and get the length — all without calling external commands like sed, cut, or awk.

Key point: Parameter expansion happens inside the shell — it’s fast, it’s portable, and it doesn’t spawn a subprocess. Learn these forms and you’ll write shorter, faster scripts.


a – what is parameter expansion in shell scripts part 1

Parameter expansion in a shell script allows you to manipulate variable values in various ways — substring extraction, default value assignment, and pattern replacement.

Basic syntax:

${variable}
${variable:...}
${variable/...}

The braces ${...} are required when you apply an operation to a variable. Without them, the shell only knows the variable’s name and can’t tell where it ends.

$ name="Kronos"
$ echo "$name_file"       # looks for $name_file → empty
$ echo "${name}_file"     # Kronos_file ✅

Common parameter expansions:

1. Substring extraction:

FormMeaning
${var:position}Substring from position to end
${var:position:length}Substring of length starting at position
$ var="hello world"
$ echo "${var:2}"
[ llo world ]
$ echo "${var:2:5}"
[ llo w ]

Note: Positions start at 0. So ${var:0:5} gives the first five characters.

$ echo "${var:0:5}"
[ hello ]
$ echo "${var:6}"
[ world ]

Negative offsets count from the end (bash 4.2+, needs a space after the colon):

$ echo "${var: -5}"
[ world ]
$ echo "${var: -5:3}"
[ wor ]

2. Default value assignment:

FormMeaning
${var:-word}If unset or empty, use word — don’t assign
${var:=word}If unset or empty, use word — assign it
$ unset unset_var
$ default_var=${unset_var:-"default_value"}
$ echo "$default_var"
[ default_value ]
$ echo "${unset_var:-empty}"
[ empty ]

The difference between :- and :=:

# :- doesn't change the variable
$ unset x
$ echo "${x:-fallback}"
[ fallback ]
$ echo "${x:-still unset}"
[ still unset ]

# := assigns the variable
$ unset x
$ echo "${x:=fallback}"
[ fallback ]
$ echo "${x:-now set}"
[ fallback ]

More default-related forms:

FormMeaning
${var:-word}Use word if unset or null
${var:=word}Assign word if unset or null
${var:+word}Use word if set, else empty
${var:?message}Error and exit if unset or null
$ unset x
$ echo "${x:+set}"
[ ]
$ x=hello
$ echo "${x:+set}"
[ set ]

$ unset x
$ echo "${x:?must be set}"
[ bash: x: must be set ]

${var:?message} is the classic way to validate required variables in scripts:

#!/bin/bash
: "${CONFIG:?CONFIG must be set}"
echo "Using config: $CONFIG"

If CONFIG is unset, the script exits with the message. If it’s set, execution continues.

3. Parameter replacement:

These forms check whether a variable is set — but not its value:

FormBehavior
${var+word}If var is set, use word; else empty
${var?word}If var is set, use its value; else error
$ unset x
$ echo "${x+set}"
[ ]
$ x="anything"
$ echo "${x+set}"
[ set ]

$ unset y
$ echo "${y?y is required}"
[ bash: y: y is required ]

Note the subtle difference from :- and :?:

FormChecks
${var-word}Is var unset (ignores empty)
${var:-word}Is var unset or empty
${var+word}Is var set (ignores empty)
${var:+word}Is var set and non-empty
$ x=""
$ echo "${x:-default}"
[ default ]
$ echo "${x-default}"
[ ]

$ echo "${x:+set}"
[ ]
$ echo "${x+set}"
[ set ]

The : variants treat empty as “not there”; the non-colon variants only care whether the variable has ever been assigned.


b – what is parameter expansion in shell scripts part 2

4. Length of a parameter:

FormMeaning
${#var}Length of the variable’s value
$ var="hello world"
$ length=${#var}
$ echo "Length of the variable: $length"
[ Length of the variable: 11 ]

Works for arrays too:

$ arr=(a b c d)
$ echo "${#arr[@]}"
[ 4 ]
$ echo "${#arr[0]}"
[ 1 ]

5. Pattern replacement:

FormMeaning
${var/pattern/string}Replace first occurrence
${var//pattern/string}Replace all occurrences
${var/#pattern/string}Replace at the start only
${var/%pattern/string}Replace at the end only
$ var="hello world"
$ replace_var=${var/world/universe}
$ echo "After replacing 'world' with 'universe': $replace_var"
[ After replacing 'world' with 'universe': hello universe ]

$ pattern_replace_var=${var//o/O}
$ echo "After replacing all 'o' with 'O': $pattern_replace_var"
[ After replacing all 'o' with 'O': hellO wOrld ]

Examples of each variant:

$ s="foo bar foo baz foo"

$ echo "${s/foo/X}"
[ X bar foo baz foo ]            # first only

$ echo "${s//foo/X}"
[ X bar X baz X ]                # all

$ echo "${s/#foo/X}"
[ X bar foo baz foo ]            # start only

$ echo "${s/%foo/X}"
[ foo bar foo baz X ]            # end only

Patterns are globs, not regex:

The pattern in ${var/pattern/string} is a glob pattern — *, ?, [...] — not a regular expression:

$ file="report.txt"
$ echo "${file/.*/.bak}"
[ report.bak ]

$ name="alice.smith"
$ echo "${name/./_}"
[ alice_smith ]

To remove instead of replace, use an empty string:

$ phone="555-123-4567"
$ echo "${phone//-/}"
[ 5551234567 ]

6. Remove matching prefix/suffix:

FormRemoves
${var#pattern}Shortest matching prefix
${var##pattern}Longest matching prefix
${var%pattern}Shortest matching suffix
${var%%pattern}Longest matching suffix
$ var="hello world"
$ remove_prefix=${var#hello }
$ echo "Remove shortest matching prefix 'hello ': $remove_prefix"
[ Remove shortest matching prefix 'hello ': world ]

$ remove_suffix=${var% world}
$ echo "Remove shortest matching suffix ' world': $remove_suffix"
[ Remove shortest matching suffix ' world': hello ]

The power of # and % — extracting paths and extensions:

$ path="/home/kronos/documents/report.txt"

# Get the filename (remove longest prefix ending in /)
$ echo "${path##*/}"
[ report.txt ]

# Get the directory (remove shortest suffix starting with /)
$ echo "${path%/*}"
[ /home/kronos/documents ]

# Get the extension (remove longest prefix ending in .)
$ echo "${path##*.}"
[ txt ]

# Get the filename without extension
$ echo "${path%.*}"
[ /home/kronos/documents/report ]

This is the idiomatic way to split paths in shell scripts — no basename or dirname needed (though those are clearer for readability).

Shortest vs longest — a concrete example:

$ file="archive.tar.gz"

$ echo "${file#*.}"
[ tar.gz ]                        # shortest prefix up to first .

$ echo "${file##*.}"
[ gz ]                            # longest prefix up to last .

$ echo "${file%.*}"
[ archive.tar ]                   # shortest suffix from last .

$ echo "${file%%.*}"
[ archive ]                       # longest suffix from first .
FormMeaningResult for archive.tar.gz
${file#*.}Remove shortest prefix matching *.tar.gz
${file##*.}Remove longest prefix matching *.gz
${file%.*}Remove shortest suffix matching .*archive.tar
${file%%.*}Remove longest suffix matching .*archive

Common real-world uses:

# Change file extension
for f in *.txt; do
    mv "$f" "${f%.txt}.md"
done

# Strip path to get filename
for path in /var/log/*.log; do
    echo "File: ${path##*/}"
done

# Remove trailing slash
dir="/home/kronos/"
echo "${dir%/}"
[ /home/kronos ]

# Remove leading ./
file="./script.sh"
echo "${file#./}"
[ script.sh ]

# Extract just the extension
for f in *; do
    echo "$f → ${f##*.}"
done

# Replace spaces with underscores
name="my file.txt"
echo "${name// /_}"
[ my_file.txt ]

# Default if empty
read -p "Name [Guest]: " name
name="${name:-Guest}"

Putting it all together:

#!/bin/bash

var="hello world"

echo "Substring from position 3: ${var:2}"
echo "Substring from position 3 to length 5: ${var:2:5}"

default_var=${unset_var:-"default_value"}
echo "Default value assigned: $default_var"

replace_var=${var/world/universe}
echo "After replacing 'world' with 'universe': $replace_var"

length=${#var}
echo "Length of the variable: $length"

pattern_replace_var=${var//o/O}
echo "After replacing all 'o' with 'O': $pattern_replace_var"

remove_prefix=${var#hello }
echo "Remove shortest matching prefix 'hello ': $remove_prefix"
remove_suffix=${var% world}
echo "Remove shortest matching suffix ' world': $remove_suffix"
$ ./expand.sh
[ Substring from position 3: llo world ]
[ Substring from position 3 to length 5: llo w ]
[ Default value assigned: default_value ]
[ After replacing 'world' with 'universe': hello universe ]
[ Length of the variable: 11 ]
[ After replacing all 'o' with 'O': hellO wOrld ]
[ Remove shortest matching prefix 'hello ': world ]
[ Remove shortest matching suffix ' world': hello ]

Complete Example Session

# ============================================
# PART 1: SUBSTRING
# ============================================

$ var="hello world"
$ echo "${var:2}"
[ llo world ]
$ echo "${var:2:5}"
[ llo w ]
$ echo "${var:0:5}"
[ hello ]
$ echo "${var:6}"
[ world ]

# ============================================
# PART 2: NEGATIVE OFFSETS
# ============================================

$ echo "${var: -5}"
[ world ]
$ echo "${var: -5:3}"
[ wor ]

# ============================================
# PART 3: DEFAULTS
# ============================================

$ unset x
$ echo "${x:-default}"
[ default ]
$ echo "${x:=assigned}"
[ assigned ]
$ echo "$x"
[ assigned ]

# ============================================
# PART 4: REQUIRED VARIABLES
# ============================================

$ unset CONFIG
$ echo "${CONFIG:?CONFIG is required}"
[ bash: CONFIG: CONFIG is required ]

# ============================================
# PART 5: ALTERNATE VALUES
# ============================================

$ unset x
$ echo "${x:+set}"
[ ]
$ x=hello
$ echo "${x:+set}"
[ set ]

# ============================================
# PART 6: LENGTH
# ============================================

$ var="hello world"
$ echo "${#var}"
[ 11 ]

$ arr=(a b c d)
$ echo "${#arr[@]}"
[ 4 ]

# ============================================
# PART 7: REPLACE FIRST
# ============================================

$ s="foo bar foo baz foo"
$ echo "${s/foo/X}"
[ X bar foo baz foo ]

# ============================================
# PART 8: REPLACE ALL
# ============================================

$ echo "${s//foo/X}"
[ X bar X baz X ]

# ============================================
# PART 9: REPLACE AT START
# ============================================

$ echo "${s/#foo/X}"
[ X bar foo baz foo ]

# ============================================
# PART 10: REPLACE AT END
# ============================================

$ echo "${s/%foo/X}"
[ foo bar foo baz X ]

# ============================================
# PART 11: REMOVE PREFIX
# ============================================

$ var="hello world"
$ echo "${var#hello }"
[ world ]
$ echo "${var#* }"
[ world ]
$ echo "${var##* }"
[ world ]

# ============================================
# PART 12: REMOVE SUFFIX
# ============================================

$ echo "${var% world}"
[ hello ]
$ echo "${var% *}"
[ hello ]
$ echo "${var%% *}"
[ hello ]

# ============================================
# PART 13: PATH AND FILENAME
# ============================================

$ path="/home/kronos/documents/report.txt"
$ echo "${path##*/}"
[ report.txt ]
$ echo "${path%/*}"
[ /home/kronos/documents ]
$ echo "${path##*.}"
[ txt ]
$ echo "${path%.*}"
[ /home/kronos/documents/report ]

# ============================================
# PART 14: EXTENSION MANIPULATION
# ============================================

$ file="archive.tar.gz"
$ echo "${file#*.}"
[ tar.gz ]
$ echo "${file##*.}"
[ gz ]
$ echo "${file%.*}"
[ archive.tar ]
$ echo "${file%%.*}"
[ archive ]

# ============================================
# PART 15: CHANGE EXTENSION
# ============================================

$ f="report.txt"
$ echo "${f%.txt}.md"
[ report.md ]

# ============================================
# PART 16: REMOVE TRAILING SLASH
# ============================================

$ dir="/home/kronos/"
$ echo "${dir%/}"
[ /home/kronos ]

# ============================================
# PART 17: REPLACE SPACES
# ============================================

$ name="my file.txt"
$ echo "${name// /_}"
[ my_file.txt ]

# ============================================
# PART 18: LOOP WITH EXTENSION CHANGE
# ============================================

$ touch a.txt b.txt c.txt
$ for f in *.txt; do
>     echo "mv $f ${f%.txt}.md"
> done
[ mv a.txt a.md ]
[ mv b.txt b.md ]
[ mv c.txt c.md ]

# ============================================
# PART 19: DEFAULT WITH PROMPT
# ============================================

$ read -p "Name [Guest]: " name
Name:
$ name="${name:-Guest}"
$ echo "$name"
[ Guest ]

# ============================================
# PART 20: FULL SCRIPT
# ============================================

$ cat > expand.sh << 'EOF'
#!/bin/bash

var="hello world"

echo "Substring from position 3: ${var:2}"
echo "Substring from position 3 to length 5: ${var:2:5}"

default_var=${unset_var:-"default_value"}
echo "Default value assigned: $default_var"

replace_var=${var/world/universe}
echo "After replacing 'world' with 'universe': $replace_var"

length=${#var}
echo "Length of the variable: $length"

pattern_replace_var=${var//o/O}
echo "After replacing all 'o' with 'O': $pattern_replace_var"

remove_prefix=${var#hello }
echo "Remove shortest matching prefix 'hello ': $remove_prefix"
remove_suffix=${var% world}
echo "Remove shortest matching suffix ' world': $remove_suffix"
EOF
$ chmod +x expand.sh
$ ./expand.sh
[ Substring from position 3: llo world ]
[ Substring from position 3 to length 5: llo w ]
[ Default value assigned: default_value ]
[ After replacing 'world' with 'universe': hello universe ]
[ Length of the variable: 11 ]
[ After replacing all 'o' with 'O': hellO wOrld ]
[ Remove shortest matching prefix 'hello ': world ]
[ Remove shortest matching suffix ' world': hello ]

Quick Reference

Substring

FormMeaning
${var:offset}From offset to end
${var:offset:length}Length chars from offset
${var: -N}Last N characters
${var:0:N}First N characters

Defaults

FormMeaning
${var:-word}word if unset or empty
${var:=word}Assign word if unset or empty
${var:+word}word if set, else empty
${var:?msg}Error if unset or empty
${var-word}word if unset (empty OK)
${var=word}Assign if unset (empty OK)
${var+word}word if set (empty OK)
${var?msg}Error if unset (empty OK)

Length

FormMeaning
${#var}Length of value
${#arr[@]}Number of array elements
${#arr[0]}Length of first element

Replace

FormMeaning
${var/pat/str}Replace first match
${var//pat/str}Replace all matches
${var/#pat/str}Replace at start
${var/%pat/str}Replace at end

Remove

FormMeaning
${var#pat}Remove shortest prefix
${var##pat}Remove longest prefix
${var%pat}Remove shortest suffix
${var%%pat}Remove longest suffix

Case Conversion (bash 4+)

FormMeaning
${var^}Capitalize first
${var^^}UPPERCASE ALL
${var,}lowercase first
${var,,}lowercase all

Path and File Idioms

IdiomResult
${path##*/}Filename
${path%/*}Directory
${file##*.}Extension
${file%.*}Name without extension
${file%.txt}.mdChange extension
${dir%/}Remove trailing slash
${file#./}Remove leading ./

Best Practices

✅ Do This:

# Use :- for defaults
name="${1:-Guest}"                     # ✅

# Use := to assign
: "${CONFIG:=/etc/default.conf}"       # ✅

# Use :? for required variables
: "${API_KEY:?API_KEY must be set}"    # ✅

# Use # and % for path manipulation
file="${path##*/}"                     # ✅
dir="${path%/*}"                       # ✅
ext="${file##*.}"                      # ✅

# Use ${#var} for length
if [ "${#password}" -lt 8 ]; then ...  # ✅

# Use // for global replace
clean="${input//[^a-zA-Z0-9]/}"        # ✅

# Use %% for extension removal
base="${file%%.*}"                     # ✅

# Quote when safe
echo "${var:-default}"                 # ✅

# Use negative offsets carefully (space!)
echo "${var: -5}"                      # ✅

❌ Don’t Do This:

# Don't forget the space with negative offset
echo "${var:-5}"                       # ❌ this is a default!
echo "${var: -5}"                      # ✅ last 5 chars

# Don't mix up : and no-colon forms
echo "${x-default}"                    # ⚠️  only if UNSET (empty passes)
echo "${x:-default}"                   # ✅ if unset OR empty

# Don't use regex syntax in patterns
echo "${var/[0-9]/X}"                  # ⚠️  [] is a glob class, not regex

# Don't forget quotes when assigning
name=${1:-Guest}                       # ⚠️  breaks on spaces
name="${1:-Guest}"                     # ✅

# Don't use # with no pattern
echo "${var#}"                         # ⚠️  removes nothing

# Don't assume case conversion works in old bash
echo "${var^^}"                        # ❌ pre-bash 4

Common Pitfalls

PitfallProblemSolution
${var:-5} vs ${var: -5}Default vs substringSpace after :
: vs no colonEmpty vs unsetUse : for “unset or empty”
Unquoted expansionWord splittingQuote "${...}"
Pattern = regexGlob, not regexUse globs (*, ?, [...])
## vs #Longest vs shortestChoose deliberately
Missing bracesWrong variable nameUse ${var}
Offset out of rangeEmpty resultCheck length first
Case conv pre-bash 4Not supportedUpgrade or use tr

Real-World Examples

1. Substring from Position

#!/bin/bash
var="hello world"
echo "${var:2}"
[ llo world ]

2. Substring with Length

#!/bin/bash
var="hello world"
echo "${var:2:5}"
[ llo w ]

3. Default Value

#!/bin/bash
default_var=${unset_var:-"default_value"}
echo "$default_var"
[ default_value ]

4. Replace Once

#!/bin/bash
var="hello world"
echo "${var/world/universe}"
[ hello universe ]

5. Length

#!/bin/bash
var="hello world"
echo "${#var}"
[ 11 ]

6. Replace All

#!/bin/bash
var="hello world"
echo "${var//o/O}"
[ hellO wOrld ]

7. Remove Prefix

#!/bin/bash
var="hello world"
echo "${var#hello }"
[ world ]

8. Remove Suffix

#!/bin/bash
var="hello world"
echo "${var% world}"
[ hello ]

9. Filename from Path

#!/bin/bash
path="/var/log/syslog"
echo "${path##*/}"
[ syslog ]

10. Directory from Path

#!/bin/bash
path="/var/log/syslog"
echo "${path%/*}"
[ /var/log ]

11. Change Extension

#!/bin/bash
file="report.txt"
echo "${file%.txt}.md"
[ report.md ]

12. Strip Extension

#!/bin/bash
file="archive.tar.gz"
echo "${file%%.*}"
[ archive ]
echo "${file%.*}"
[ archive.tar ]

13. Remove Trailing Slash

#!/bin/bash
dir="/home/kronos/"
echo "${dir%/}"
[ /home/kronos ]

14. Replace Spaces

#!/bin/bash
name="my file.txt"
echo "${name// /_}"
[ my_file.txt ]

15. Uppercase

#!/bin/bash
name="kronos"
echo "${name^^}"
[ KRONOS ]

16. Lowercase

#!/bin/bash
name="KRONOS"
echo "${name,,}"
[ kronos ]

17. Required Variable

#!/bin/bash
: "${API_KEY:?API_KEY must be set}"
echo "Using key: $API_KEY"

18. Validate Non-Empty

#!/bin/bash
if [ -z "${1:-}" ]; then
    echo "Usage: $0 <name>" >&2
    exit 1
fi
echo "Hello, $1"

19. Batch Rename

#!/bin/bash
for f in *.txt; do
    mv "$f" "${f%.txt}.md"
done

20. Full Example

#!/bin/bash

var="hello world"

echo "Substring from position 3: ${var:2}"
echo "Substring from position 3 to length 5: ${var:2:5}"

default_var=${unset_var:-"default_value"}
echo "Default value assigned: $default_var"

replace_var=${var/world/universe}
echo "After replacing 'world' with 'universe': $replace_var"

length=${#var}
echo "Length of the variable: $length"

pattern_replace_var=${var//o/O}
echo "After replacing all 'o' with 'O': $pattern_replace_var"

remove_prefix=${var#hello }
echo "Remove shortest matching prefix 'hello ': $remove_prefix"
remove_suffix=${var% world}
echo "Remove shortest matching suffix ' world': $remove_suffix"

Visual: Parameter Expansion Types

┌──────────────────────────────────────────────┐
│           ${var...} at a glance              │
│                                              │
│  ${var}          → value                     │
│  ${var:-default} → default if unset/empty    │
│  ${var:=default} → assign default            │
│  ${var:+alt}     → alt if set                │
│  ${var:?msg}     → error if unset            │
│  ${#var}         → length                    │
│  ${var:2}        → substring from 2          │
│  ${var:2:5}      → substring, 5 chars        │
│  ${var/p/s}      → replace first             │
│  ${var//p/s}     → replace all               │
│  ${var/#p/s}     → replace at start          │
│  ${var/%p/s}     → replace at end            │
│  ${var#p}        → remove shortest prefix    │
│  ${var##p}       → remove longest prefix     │
│  ${var%p}        → remove shortest suffix    │
│  ${var%%p}       → remove longest suffix     │
│  ${var^^}        → UPPERCASE                 │
│  ${var,,}        → lowercase                 │
│                                              │
└──────────────────────────────────────────────┘

Visual: Removing Prefixes and Suffixes

┌──────────────────────────────────────────────┐
│  path = /home/kronos/docs/report.txt         │
│                                              │
│  ${path#*/}    → home/kronos/docs/report.txt │
│  ${path##*/}   → report.txt                  │
│  ${path%/*}    → /home/kronos/docs           │
│  ${path%%/*}   → (empty)                     │
│                                              │
│  # = remove from left, ## = greedy from left │
│  % = remove from right, %% = greedy from right│
│                                              │
└──────────────────────────────────────────────┘

Summary

ConceptSyntaxExample
Value${var}${name}
Length${#var}${#name}
Substring${var:2:5}${var:2}
Last N chars${var: -5}${var: -3}
Default${var:-word}${1:-Guest}
Assign default${var:=word}${x:=fallback}
Alternate${var:+word}${x:+set}
Required${var:?msg}${API_KEY:?missing}
Replace first${var/p/s}${s/foo/bar}
Replace all${var//p/s}${s// /_}
Replace at start${var/#p/s}${s/#foo/X}
Replace at end${var/%p/s}${s/%foo/X}
Remove prefix${var#pat}${path##*/}
Remove longest prefix${var##pat}${path##*/}
Remove suffix${var%pat}${file%.txt}
Remove longest suffix${var%%pat}${file%%.*}
Uppercase${var^^}${name^^}
Lowercase${var,,}${name,,}

Key takeaways:

  • Parameter expansion works inside the shell — fast, portable, no subprocess
  • Use ${var:-default} to provide a fallback when a variable is unset or empty
  • Use ${var:=default} to assign the fallback as well
  • Use ${var:?message} to enforce required variables at the top of a script
  • Use ${#var} for string length
  • Use ${var:offset:length} for substring extraction
  • Use ${var/pattern/string} to replace — // for all, /# for start, /% for end
  • Use ${var#pattern} and ${var##pattern} to strip prefixes
  • Use ${var%pattern} and ${var%%pattern} to strip suffixes
  • # and % are the secret weapons for path and extension manipulation
  • Patterns are globs, not regex — *, ?, [...]
  • Always quote — "${var}" — to avoid word splitting
  • Watch the space in negative offsets: ${var: -5}, not ${var:-5}

Remember: Parameter expansion is how you do text manipulation without spawning subprocesses. Learn the four families — substring (:offset:length), defaults (:-, :=, :?), replacement (/, //), and removal (#, ##, %, %%). The # and % forms are especially powerful for paths and filenames — ${path##*/} for the filename, ${path%/*} for the directory, ${file%.*} to strip the extension. Master these, and half your sed, cut, and basename calls disappear.

Was this helpful?


Stop using slow, ad-bloated tool sites! 🤮

🔎 Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
✅ Finance (Mortgage, Interest, Inflation)
✅ Tech (Base64, JSON, Dev Suite, IP)
✅ Health (BMI, BMR, TDEE)
✅ Productivity (Timer, Workspace, QR)

⚡️ Fast & Private
🔒 No data leaves your device
💎 100% Free

🔗 Use it now: https://tools.kandz.me
🔖 Bookmark it—you’ll need it later!