Remove a fixed prefix/suffix from a string in Bash
Solution 1
$ prefix="hell"
$ suffix="ld"
$ string="hello-world"
$ foo=${string#"$prefix"}
$ foo=${foo%"$suffix"}
$ echo "${foo}"
o-wor
This is documented in the Shell Parameter Expansion section of the manual:
${parameter#word}
${parameter##word}
The word is expanded to produce a pattern and matched according to the rules described below (see Pattern Matching). If the pattern matches the beginning of the expanded value of parameter, then the result of the expansion is the expanded value of parameter with the shortest matching pattern (the
#
case) or the longest matching pattern (the##
case) deleted. […]
${parameter%word}
${parameter%%word}
The word is expanded to produce a pattern and matched according to the rules described below (see Pattern Matching). If the pattern matches a trailing portion of the expanded value of parameter, then the result of the expansion is the value of parameter with the shortest matching pattern (the
%
case) or the longest matching pattern (the%%
case) deleted. […]
Solution 2
Using sed:
$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor
Within the sed command, the ^
character matches text beginning with $prefix
, and the trailing $
matches text ending with $suffix
.
Adrian Frühwirth makes some good points in the comments below, but sed
for this purpose can be very useful. The fact that the contents of $prefix and $suffix are interpreted by sed can be either good OR bad- as long as you pay attention, you should be fine. The beauty is, you can do something like this:
$ prefix='^.*ll'
$ suffix='ld$'
$ echo "$string" | sed -e "s/^$prefix//" -e "s/$suffix$//"
o-wor
which may be what you want, and is both fancier and more powerful than bash variable substitution. If you remember that with great power comes great responsibility (as Spiderman says), you should be fine.
A quick introduction to sed can be found at http://evc-cit.info/cit052/sed_tutorial.html
A note regarding the shell and its use of strings:
For the particular example given, the following would work as well:
$ echo $string | sed -e s/^$prefix// -e s/$suffix$//
...but only because:
- echo doesn't care how many strings are in its argument list, and
- There are no spaces in $prefix and $suffix
It's generally good practice to quote a string on the command line because even if it contains spaces it will be presented to the command as a single argument. We quote $prefix and $suffix for the same reason: each edit command to sed will be passed as one string. We use double quotes because they allow for variable interpolation; had we used single quotes the sed command would have gotten a literal $prefix
and $suffix
which is certainly not what we wanted.
Notice, too, my use of single quotes when setting the variables prefix
and suffix
. We certainly don't want anything in the strings to be interpreted, so we single quote them so no interpolation takes place. Again, it may not be necessary in this example but it's a very good habit to get into.
Solution 3
$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ #remove "hell" from "hello-world" if "hell" is found at the beginning.
$ prefix_removed_string=${string/#$prefix}
$ #remove "ld" from "o-world" if "ld" is found at the end.
$ suffix_removed_String=${prefix_removed_string/%$suffix}
$ echo $suffix_removed_String
o-wor
Notes:
#$prefix : adding # makes sure that substring "hell" is removed only if it is found in beginning. %$suffix : adding % makes sure that substring "ld" is removed only if it is found in end.
Without these, the substrings "hell" and "ld" will get removed everywhere, even it is found in the middle.
Solution 4
Do you know the length of your prefix and suffix? In your case:
result=$(echo $string | cut -c5- | rev | cut -c3- | rev)
Or more general:
result=$(echo $string | cut -c$((${#prefix}+1))- | rev | cut -c$((${#suffix}+1))- | rev)
But the solution from Adrian Frühwirth is way cool! I didn't know about that!
Solution 5
I use grep for removing prefixes from paths (which aren't handled well by sed
):
echo "$input" | grep -oP "^$prefix\K.*"
\K
removes from the match all the characters before it.

Dušan Rychnovský
Updated on March 15, 2022Comments
-
Dušan Rychnovský about 1 year
In my
bash
script I have a string and its prefix/suffix. I need to remove the prefix/suffix from the original string.For example, let's say I have the following values:
string="hello-world" prefix="hell" suffix="ld"
How do I get to the following result?
result="o-wor"
-
Salah Eddine Taouririt about 10 yearsTake a look Advanced Bash-Scripting Guide
-
tripleee over 6 yearsBe very wary when linking to the so-called Advanced Bash Scripting Guide; it contains a mixture of good advice and terrible.
-
-
pts about 10 yearsThere are also ## and %% , which remove as much as possible if $prefix or $suffix contain wildcards.
-
static_rtti about 9 yearsIs there a way to combine the two in one line? I tried
${${string#prefix}%suffix}
but it doesn't work. -
Adrian Frühwirth about 9 years@static_rtti No, unfortunately you cannot nest parameter substitution like this. I know, it's a shame.
-
static_rtti about 9 years@AdrianFrühwirth : the whole language is a shame, but it's so useful :)
-
Adrian Frühwirth about 9 yearsUnfortunately, this is bad advice for several reasons: 1) Unquoted,
$string
is subject to word splitting and globbing. 2)$prefix
and$suffix
can contain expressions thatsed
will interpret, e.g. regular expressions or the character used as delimiter which will break the whole command. 3) Callingsed
two times is not necessary (you can-e 's///' -e '///'
instead) and the pipe could also be avoided. For example, considerstring='./ *'
and/orprefix='./'
and see it break horribly due to1)
and2)
. -
Olie over 8 yearsFun note: sed can take almost anything as a delimiter. In my case, since I was parsing prefix-directories out of paths, I couldn't use
/
, so I usedsed "s#^$prefix##
, instead. (Fragility: filenames can't contain#
. Since I control the files, we're safe, there.) -
Tyler over 8 yearsWhat are there things called and where can I find a reference on them?
-
Tyler over 8 yearsNvm, "bash substitution" in Google found what I wanted.
-
Adrian Frühwirth over 8 years@Olie Filenames can contain any character except the slash and null character so unless you're in control you cannot assume a filename not to contain certain characters.
-
Olie over 8 yearsYeah, don't know what I was thinking there. iOS maybe? Dunno. Filenames can certainly contain "#". No idea why I said that. :)
-
P Daddy about 8 years@Olie: As I understood your original comment, you were saying that the limitation of your choice to use
#
as sed's delimiter meant that you couldn't handle files containing that character. -
Jason Owen over 7 yearsThis is documented in the Parameter Substitution section of the Advanced Bash-Scripting Guide: tldp.org/LDP/abs/html/parameter-substitution.html .
-
ROMANIA_engineer over 7 years@static_rtti , there is a workaround: echo
basename ${string/hell} ld
(where the grey part is between backticks) -
ccpizza almost 7 years@AdrianFrühwirth: The replacement is case sensitive. Any way to make it case insensitive?
-
Adrian Frühwirth almost 7 years@ccpizza Parameter substitution does not know such modifiers but if it's a fixed string you could always do e.g.
${foo#[Bb][Aa][Rr]}
. Not pretty but still possibly better than an unnecessary subshell/fork, depending on the situation. -
jakub.g over 6 yearsnote that magic characters are
#
and%
, not#$
and%$
-$
is part of$prefix
in example above - which might not be clear at a first glance -
basin almost 5 yearsIf
$suffix
contains pattern chars like*][
the result can be unexpected. Better quote it. Seefoo="abc*def"; suffix="*def"; echo "${foo%$suffix}"; echo "${foo%"$suffix"}"
. It will printabc* abc
-
brainplot over 4 yearsIs there a way to remove a suffix/prefix from the output of a command without assigning it first to a temporary variable? I tried something like this
${$(cmd)#"$prex"}
but doesn't work. -
ntninja about 4 yearsOT: What @static_rtti wrote does work in ZSH using that exact syntax :-)
-
DiegoSalazar about 4 yearsThanks for the Notes! qq: in your code example you also have a forward slash
/
right after the string, what is that for? -
Vijayendar Gururaja about 4 years/ separates the current string and the sub string. sub-string here is the suffix in th posted question.
-
tripleee almost 4 yearsParameter expansion is documented in the Bash reference manual. We really don't want to give people the impression that the ABS is a high-quality reference.
-
tripleee almost 4 years
grep -P
is a nonstandard extension. More power to you if it's supported on your platform, but this is dubious advice if your code needs to be reasonably portable. -
tripleee almost 4 yearsIf you are using Bash, you should probably not be using
expr
at all. It was a sort of convenient kitchen sink utility back in the days of the original Bourne shell, but is now way past its best-before date. -
Vladimir Petrakovich almost 4 years@tripleee Indeed. But I think a system with GNU Bash installed also have a grep that supports PCRE.
-
tripleee almost 4 yearsNo, MacOS for example has Bash out of the box but not GNU
grep
. Earlier versions actually had the-P
option from BSDgrep
but they removed it. -
Augustin Riedinger over 3 yearsWhat if
$prefix
has a space in the end? -
Adrian Frühwirth over 3 years@AugustinRiedinger What exactly is your question?
-
Augustin Riedinger over 3 yearsIf
prefix="Hello "
andsentence="Hello world"
,${sentence#"$prefix"}
did not seem to work, but I just tested it and now it does. :/ Apologies for the disturbance. -
usretc about 2 yearsUh, why?
expr
is old, but never changes, and will probably always be available. As long as you invoke an external binary (as opposed to using BASH expressions), grep, sed or expr are pretty much equivalent (perl / awk would be costlier). -
nt86 almost 2 yearsIn case anyone is looking for it, escape with \ also works here, i.e.
${parameter%\[*\]}
will remove [any-string-here] suffix. -
Stobor about 1 yearGreat option! Worth calling out: a key difference between this solution and the others is that if the source string does not start with prefix or end with suffix, then other solutions will not clip anything, where this solution will clip the length of the suffix away. This is not necessarily a problem, just a limitation to be aware of. If you're not sure if the string starts or ends with the prefix/suffix, simply wrap this statement in the appropriate if-statement to check before trimming.
-
Charles Duffy 11 months@JasonOwen Do consider deleting your above ABS link. Entire competing reference sources have written (notably including the BashGuide) because people (particularly, the elders of what was then the freenode #bash IRC channel) got fed up with trying to teach newbies to stop reusing bad practices they picked up from the ABS.