← Back to Bash Course | Chapter 7: String Manipulation | Lesson 6 of 10

Trimming/Removing Prefixes & Suffixes

Bash has dedicated parameter expansions for stripping a matching prefix or suffix off a string, which is exactly how you pull a filename apart from its directory path or extension.

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 ##

bash
#!/bin/bash
path="/usr/local/bin/tool"
echo "Shortest prefix removed: ${path#*/}"
echo "Longest prefix removed: ${path##*/}"

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 %%

bash
#!/bin/bash
file="archive.tar.gz"
echo "Shortest suffix removed: ${file%.*}"
echo "Longest suffix removed: ${file%%.*}"

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

bash
#!/bin/bash
filename="my.report.final.pdf"
extension=${filename##*.}
echo "Extension: $extension"

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

bash
#!/bin/bash
filename="my.report.final.pdf"
base=${filename%.*}
echo "Base name: $base"
Common Mistakes
  1. 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.
  2. 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.
  3. Assuming these operators use regex; like other pattern-based expansions, they use glob wildcards (*, ?, [...]), not full regular expressions.
Chapter Summary
  • ${s#pattern} removes the shortest match of pattern from the start of s; ${s##pattern} removes the longest match from the start.
  • ${s%pattern} removes the shortest match of pattern from the end of s; ${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 case and filename expansion.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.