Remove a fixed prefix/suffix from a string in Bash

Bash

Bash Problem Overview


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"

Bash Solutions


Solution 1 - Bash

$ 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 - Bash

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:

  1. echo doesn't care how many strings are in its argument list, and
  2. 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 - Bash

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 4 - Bash

$ 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 5 - Bash

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.

Solution 6 - Bash

Using the =~ operator:

$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ [[ "$string" =~ ^$prefix(.*)$suffix$ ]] && echo "${BASH_REMATCH[1]}"
o-wor

Solution 7 - Bash

Small and universal solution:

expr "$string" : "$prefix\(.*\)$suffix"

Solution 8 - Bash

Using @Adrian Frühwirth answer:

function strip {
    local STRING=${1#$"$2"}
    echo ${STRING%$"$2"}
}

use it like this

HELLO=":hello:"
HELLO=$(strip "$HELLO" ":")
echo $HELLO # hello

Solution 9 - Bash

NOTE: Not sure if this was possible back in 2013 but it's certainly possible today (10 Oct 2021) so adding another option ...


Since we're dealing with known fixed length strings (prefix and suffix) we can use a bash substring to obtain the desired result with a single operation.

Inputs:

string="hello-world"
prefix="hell"
suffix="ld"

Plan:

  • bash substring syntax: ${string:<start>:<length>}
  • skipping over prefix="hell" means our <start> will be 4
  • <length> will be total length of string (${#string}) minus the lengths of our fixed length strings (4 for hell / 2 for ld)

This gives us:

$ echo "${string:4:(${#string}-4-2)}"
o-wor

NOTE: the parens can be removed and still obtain the same result


If the values of prefix and suffix are unknown, or could vary, we can still use this same operation but replace 4 and 2 with ${#prefix} and ${#suffix}, respectively:

$ echo "${string:${#prefix}:${#string}-${#prefix}-${#suffix}}"
o-wor

Solution 10 - Bash

I would make use of capture groups in regex:

$ string="hello-world"
$ prefix="hell"
$ suffix="ld"
$ set +H # Disables history substitution, can be omitted in scripts.
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/" <<< $string
o-wor
$ string1=$string$string
$ perl -pe "s/${prefix}((?:(?!(${suffix})).)*)${suffix}/\1/g" <<< $string1
o-woro-wor

((?:(?!(${suffix})).)*) makes sure that the content of ${suffix} will be excluded from the capture group. In terms of example, it's the string equivalent to [^A-Z]*. Otherwise you will get:

$ perl -pe "s/${prefix}(.*)${suffix}/\1/g" <<< $string1
o-worldhello-wor

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDušan Rychnovsk&#253;View Question on Stackoverflow
Solution 1 - BashAdrian FrühwirthView Answer on Stackoverflow
Solution 2 - BashChris KolodinView Answer on Stackoverflow
Solution 3 - Bashtommy.carstensenView Answer on Stackoverflow
Solution 4 - BashVijayendar GururajaView Answer on Stackoverflow
Solution 5 - BashVladimir PetrakovichView Answer on Stackoverflow
Solution 6 - BashMartin - マーチンView Answer on Stackoverflow
Solution 7 - BashTosi DoView Answer on Stackoverflow
Solution 8 - Bashmath2001View Answer on Stackoverflow
Solution 9 - Bashmarkp-fusoView Answer on Stackoverflow
Solution 10 - BashBayouView Answer on Stackoverflow