Trimming/Removing Prefixes & Suffixes
In this page:
Removing a Prefix with # and ##
${s#pattern} strips the shortest text at the start of s that matches pattern, while ${s##pattern} strips the longest such match. The difference matters when the pattern could match varying amounts of text, such as a wildcard followed by a fixed delimiter.
Example: Removing a Prefix with # and ##
#!/bin/bash
path="/usr/local/bin/tool"
echo "Shortest prefix removed: ${path#*/}"
echo "Longest prefix removed: ${path##*/}"
Login to try C/C++/Java/PHP code in the editor
Removing a Suffix with % and %%
${s%pattern} strips the shortest text at the end of s matching pattern, and ${s%%pattern} strips the longest such match. This mirrors the prefix operators but works from the end of the string instead.
Example: Removing a Suffix with % and %%
#!/bin/bash
file="archive.tar.gz"
echo "Shortest suffix removed: ${file%.*}"
echo "Longest suffix removed: ${file%%.*}"
Login to try C/C++/Java/PHP code in the editor
Extracting a File Extension
Combining ##*. (longest-prefix removal up to the last dot) is the standard idiom for extracting just a file's extension, since it removes everything up through the final .. This works correctly even for filenames with multiple dots.
Example: Extracting a File Extension
#!/bin/bash
filename="my.report.final.pdf"
extension=${filename##*.}
echo "Extension: $extension"
Login to try C/C++/Java/PHP code in the editor
Extracting a Base Name Without Extension
${filename%.*} removes the shortest suffix matching .* (a dot followed by anything), which strips off just the final extension while keeping the rest of the filename, including any earlier dots, intact. This is the counterpart to extension extraction.
Example: Extracting a Base Name Without Extension
#!/bin/bash
filename="my.report.final.pdf"
base=${filename%.*}
echo "Base name: $base"
Login to try C/C++/Java/PHP code in the editor
- Confusing
#/##(remove from the front) with%/%%(remove from the back); a memory trick is#looks like it's at the start of a comment (front),%is often used for percentages/endings. - Mixing up the single vs. double forms;
#/%remove the *shortest* matching prefix/suffix, while##/%%remove the *longest* matching one -- this matters a lot when the pattern could match at multiple lengths. - Assuming these operators use regex; like other pattern-based expansions, they use glob wildcards (
*,?,[...]), not full regular expressions.
${s#pattern}removes the shortest match ofpatternfrom the start ofs;${s##pattern}removes the longest match from the start.${s%pattern}removes the shortest match ofpatternfrom the end ofs;${s%%pattern}removes the longest match from the end.- A classic use is extracting a filename's extension (
${file##*.}) or its base name without extension (${file%.*}). - These operate on glob patterns, the same wildcard syntax used in
caseand filename expansion.
Chapter Quiz — Complete all 10 topics to unlock
0/10 topics done
Complete these topics first: