Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I would like to delete the last character of a string, I tried this little script :

#! /bin/sh 

t="lkj"
t=${t:-2}
echo $t

but it prints "lkj", what I am doing wrong?

share|improve this question

12 Answers 12

up vote 51 down vote accepted

In a POSIX shell, the syntax ${t:-2} means something different - it expands to the value of t if t is set and non null, and otherwise to the value 2. To trim a single character by parameter expansion, the syntax you probably want is ${t%?}

Note that in ksh93, bash or zsh, ${t:(-2)} or ${t: -2} (note the space) are legal as a substring expansion but are probably not what you want, since they return the substring starting at a position 2 characters in from the end (i.e. it removes the first character i of the string ijk).

See the Shell Parameter Expansion section of the Bash Reference Manual for more info:

share|improve this answer
1  
Would you care to explain what is the magic behind '%?' ? – afraisse Apr 14 at 15:36
3  
@afraisse ${parameter%word} removes the shortest suffix pattern matching word - see the Parameter Expansion section of man bash – steeldriver Apr 14 at 18:15

With bash 4.2 and above, you can do:

${var::-1}

Example:

$ a=123
$ echo "${a::-1}"
12

Notice that for older bash ( for example, bash 3.2.5 on OS X), you should leave spaces between and after colons:

${var: : -1}
share|improve this answer
9  
This works for bash version 4.2-alpha and above, too bad the version I have access to is earlier. :-/ – h.j.k. Jul 14 '14 at 3:46
    
Notice that for older bash ( for example, bash 3.2.5 on OS X), you should leave spaces between and after colons ${var: : -1} – iamaziz May 8 at 5:37
    
@iamaziz: From bash changelog, the negative length in ${var:offset:lenght} was added only in bash 4.2. Maybe OSX add its own patch for bash. – cuonglm May 8 at 5:59
    
@cuonglm perhaps. I am on El Capitan and it only works if I either leave spaces, or explicitly put the start and end e.g. ${v:2:-1}. – iamaziz May 8 at 6:38
    
@iamaziz: How about ${v::(-1)} or ${v:: -1}? – cuonglm May 8 at 6:49

for removing the last n characters from a line that makes no use of sed OR awk:

> echo lkj | rev | cut -c (n+1)- | rev

so for example you can delete the last character one character using this:

> echo lkj | rev | cut -c 2- | rev

> lk

from rev manpage:

DESCRIPTION
The rev utility copies the specified files to the standard output, reversing the order of characters in every line. If no files are speci- fied, the standard input is read.

UPDATE:

if you don't know the length of the string, try:

$ x="lkj"
$ echo "${x%?}"
lk
share|improve this answer
1  
Thank you for your response! The problem is that I don't know the size of the string, and are you sure that there isn't an easier way to do it? – user3581976 Jul 13 '14 at 13:53
    
see Updates @user3581976 – Networker Jul 13 '14 at 14:01
t=lkj
echo ${t:0:${#t}-1}

You get a substring from 0 to the string length -1. Note however that this substraction is bash specific, and won't work on other shells.

For instance, dash isn't able to parse even

echo ${t:0:$(expr ${#t} - 1)}

For example, on Ubuntu, /bin/sh is dash

share|improve this answer

A few options depending on the shell:

  • POSIX: t=${t%?}
  • Bourne: t=`expr " $t" : ' \(.*\).'`
  • zsh/yash: t=${t[1,-2]}
  • bash/zsh: t=${t:0:-1}
  • ksh93/bash/zsh/mksh: t=${t:0:${#t}-1}
  • ksh93/bash/zsh/mksh: t=${t/%?}
  • ksh93: t=${t/~(E).$/}
share|improve this answer
    
Nice and thorough! But... I assume all of those shells support POSIX, so everyone should just use that one to be the most portable. Smallest character count, too! – Russ Sep 16 at 3:20
    
@Russ, t=${t%?} is not Bourne but you're not likely to come across a Bourne shell nowadays. ${t%?} does work in all the other ones though. – Stéphane Chazelas Sep 16 at 6:53

You can also use head to print out all but the last character.

$ s='i am a string'
$ news=$(echo -n $s | head -c -1)
$ echo $news
i am a strin

But unfortunately some versions of head do not include the leading - option. This is the case for the head that comes with OS X.

share|improve this answer

Using sed it should be as fast as

sed 's/.$//'

Your single echo is then echo ljk | sed 's/.$//'.
Using this, the 1-line string could be any size.

share|improve this answer
    
Note that in the general case, it doesn't delete the last character of the string, but the last character of every line of the string. – Stéphane Chazelas Feb 1 at 10:23

It is easy enough to do using regular expression:

n=2
echo "lkj" | sed "s/\(.*\).\{$n\}/\1/"
share|improve this answer

Thank you SteelDriver and Networker!

if you don't know the length of the string, try:

$ x="lkj"
$ echo "${x%?}"
lk
share|improve this answer

Some refinements. To remove more than one character, you can add multiple question marks. For example, to remove the last two characters from the variable: $SRC_IP_MSG, you can use:

SRC_IP_MSG=${SRC_IP_MSG%??}
share|improve this answer

The most portable, and shortest, answer is almost certainly:

${t%?}

This works in bash, sh, ash, dash, busybox/ash, zsh, ksh, etc.

It works by using old-school shell parameter expansion. Specifically, the % specifies to remove the smallest matching suffix of parameter t that matches the glob pattern ? (ie: any character).

See "Remove Smallest Suffix Pattern" here for a (much) more detailed explanation and more background. Also see the docs for your shell (eg: man bash) under "parameter expansion".


As a side note, if you wanted to remove the first character instead, you would use ${t#?}, since # matches from the front of the string (prefix) instead of the back (suffix).

Also worth noting is that both % and # have %% and ## versions, which match the longest version of the given pattern instead of the shortest. Both ${t%%?} and ${t##?} would do the same as their single operator in this case, though (so don't add the useless extra character). This is because the given ? pattern only matches a single character. Mix in a * with some non-wildcards and things get more interesting with %% and ##.

Understanding parameter expansions, or at least knowing about their existence and knowing how to look them up, is incredibly useful for writing and deciphering shell scripts of many flavors. Parameter expansions often look like arcane shell voodoo to many people because... well... they are arcane shell voodoo (although pretty well documented if you know to look for "parameter expansion"). Definitely good to have in the tool belt when you're stuck in a shell, though.

share|improve this answer

In ksh:

echo ${ORACLE_SID/%?/}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.