How to remove last n characters from a string in Bash?

Bash

Bash Problem Overview


I have a variable var in a Bash script holding a string, like:

echo $var
"some string.rtf"

I want to remove the last 4 characters of this string and assign the result to a new variable var2, so that

echo $var2
"some string"

How can I do this?

Bash Solutions


Solution 1 - Bash

You can do like this (in bash v4 and higher):

#!/bin/bash

v="some string.rtf"

v2=${v::-4}

echo "$v --> $v2"

Note: macos uses bash 3.x by default

Solution 2 - Bash

To remove four characters from the end of the string use ${var%????}.

To remove everything after and including the final . use ${var%.*}.

Solution 3 - Bash

First, it's usually better to be explicit about your intent. So if you know the string ends in .rtf, and you want to remove that .rtf, you can just use var2=${var%.rtf}. One potentially-useful aspect of this approach is that if the string doesn't end in .rtf, it is not changed at all; var2 will contain an unmodified copy of var.

If you want to remove a filename suffix but don't know or care exactly what it is, you can use var2=${var%.*} to remove everything starting with the last .. Or, if you only want to keep everything up to but not including the first ., you can use var2=${var%%.*}. Those options have the same result if there's only one ., but if there might be more than one, you get to pick which end of the string to work from. On the other hand, if there's no . in the string at all, var2 will again be an unchanged copy of var.

If you really want to always remove a specific number of characters, here are some options.

You tagged this bash specifically, so we'll start with bash builtins. The one which has worked the longest is the same suffix-removal syntax I used above: to remove four characters, use var2=${var%????}. Or to remove four characters only if the first one is a dot, use var2=${var%.???}, which is like var2=${var%.*} but only removes the suffix if the part after the dot is exactly three characters. As you can see, to count characters this way, you need one question mark per unknown character removed, so this approach gets unwieldy for larger substring lengths.

An option in newer shell versions is substring extraction: var2=${var:0:${#var}-4}. Here you can put any number in place of the 4 to remove a different number of characters. The ${#var} is replaced by the length of the string, so this is actually asking to extract and keep (length - 4) characters starting with the first one (at index 0). With this approach, you lose the option to make the change only if the string matches a pattern; no matter what the actual value of the string is, the copy will include all but its last four characters.

You can leave the start index out; it defaults to 0, so you can shorten that to just var2=${var::${#var}-4}. In fact, newer versions of bash (specifically 4+, which means the one that ships with MacOS won't work) recognize negative lengths as the index of the character to stop at, counting back from the end of the string. So in those versions you can get rid of the string-length expression, too: var2=${var::-4}.

If you're not actually using bash but some other POSIX-type shell, the pattern-based suffix removal with % will still work – even in plain old dash, where the index-based substring extraction won't. Ksh and zsh do both support substring extraction, but require the explicit 0 start index; zsh also supports the negative end index, while ksh requires the length expression. Note that zsh, which indexes arrays starting at 1, nonetheless indexes strings starting at 0 if you use this bash-compatible syntax. But zsh also allows you to treat scalar parameters as if they were arrays of characters, in which case the substring syntax uses a 1-based count and places the start and (inclusive) end positions in brackets separated by commas: var2=$var[1,-5].

Instead of using built-in shell parameter expansion, you can of course run some utility program to modify the string and capture its output with command substitution. There are several commands that will work; one is var2=$(sed 's/.\{4\}$//' <<<"$var").

Solution 4 - Bash

What worked for me was:

echo "hello world" | rev | cut -c5- | rev
# hello w

But I used it to trim lines in a file so that's why it looks awkward. The real use was:

cat somefile | rev | cut -c5- | rev

cut only gets you as far as trimming from some starting position, which is bad if you need variable length rows. So this solution reverses (rev) the string and now we relate to its ending position, then uses cut as mentioned, and reverses (again, rev) it back to its original order.

Solution 5 - Bash

Using Variable expansion/Substring replacement:

>${var/%Pattern/Replacement} > > If suffix of var matches Pattern, then substitute Replacement for Pattern.

So you can do:

~$ echo ${var/%????/}
some string

Alternatively,

If you have always the same 4 letters

~$ echo ${var/.rtf/}
some string

If it's always ending in .xyz:

~$ echo ${var%.*}
some string

You can also use the length of the string:

~$ len=${#var}
~$ echo ${var::len-4}
some string

or simply echo ${var::-4}

Solution 6 - Bash

You could use sed,

sed 's/.\{4\}$//' <<< "$var"

EXample:

$ var="some string.rtf"
$ var1=$(sed 's/.\{4\}$//' <<< "$var")
$ echo $var1
some string

Solution 7 - Bash

This also can do the job:

... | head -c -1
-c, --bytes=[-]NUM
              print the first NUM bytes of each file; with the leading '-', print all but the last NUM bytes of each file

Solution 8 - Bash

I tried the following and it worked for me:

#! /bin/bash

var="hello.c"
length=${#var}
endindex=$(expr $length - 4)
echo ${var:0:$endindex}

Output: hel

Solution 9 - Bash

In this case you could use basename assuming you have the same suffix on the files you want to remove.

Example:

basename -s .rtf "some string.rtf"

>This will return "some string"

If you don't know the suffix, and want it to remove everything after and including the last dot:

f=file.whateverthisis
basename "${f%.*}"

> outputs "file"

% means chop, . is what you are chopping, * is wildcard

Solution 10 - Bash

Hope the below example will help,

echo ${name:0:$((${#name}-10))} --> ${name:start:len}

  • In above command, name is the variable.
  • start is the string starting point
  • len is the length of string that has to be removed.

Example:

    read -p "Enter:" name
    echo ${name:0:$((${#name}-10))}

Output:

    Enter:Siddharth Murugan
    Siddhar

Note: Bash 4.2 added support for negative substring

Solution 11 - Bash

This worked for me by calculating size of string.
It is easy you need to echo the value you need to return and then store it like below

removechars(){
        var="some string.rtf"
        size=${#var}
        echo ${var:0:size-4}  
    }
    removechars
    var2=$?

>some string

Solution 12 - Bash

The top answer doesn't work for me, because mac os x ships with a different version of bash.

I use sed like so:

var2=`echo $var2 | sed 's/.$//'`

removes the last character

var2=`echo $var2 | sed 's/..$//'`

removes the last 2 characters.

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
QuestionbeckoView Question on Stackoverflow
Solution 1 - BashonurView Answer on Stackoverflow
Solution 2 - BashEtan ReisnerView Answer on Stackoverflow
Solution 3 - BashMark ReedView Answer on Stackoverflow
Solution 4 - BashReut SharabaniView Answer on Stackoverflow
Solution 5 - BashfredtantiniView Answer on Stackoverflow
Solution 6 - BashAvinash RajView Answer on Stackoverflow
Solution 7 - BashStepan SeliukView Answer on Stackoverflow
Solution 8 - BashPriya JainView Answer on Stackoverflow
Solution 9 - BashGigiView Answer on Stackoverflow
Solution 10 - BashSiddharth MuruganView Answer on Stackoverflow
Solution 11 - BashSaurabhView Answer on Stackoverflow
Solution 12 - BashVictor PudeyevView Answer on Stackoverflow