thinker3
thinker3

Reputation: 13291

How to get the last character of a string in a shell?

I have written the following lines to get the last character of a string:

str=$1
i=$((${#str}-1))
echo ${str:$i:1}

It works for abcd/:

$ bash last_ch.sh abcd/
/

It does not work for abcd*:

$ bash last_ch.sh abcd*
array.sh assign.sh date.sh dict.sh full_path.sh last_ch.sh

It lists the files in the current folder.

Upvotes: 203

Views: 296832

Answers (11)

quickshiftin
quickshiftin

Reputation: 69611

Per @perreal, quoting variables is important, but because I read this post like five times before finding a simpler approach to the question at hand in the comments...

str='abcd/'
echo "${str: -1}"
=> /

Alternatively use ${str:0-1} as pointed out in the comments.

str='abcd*'
echo "${str:0-1}"
=> *

Note: The extra space in ${str: -1} is necessary, otherwise ${str:-1} would result in 1 being taken as the default value if str is null or empty.

${parameter:-word}
       Use Default Values.  If parameter is unset or null, the
       expansion of word is substituted.  Otherwise, the value of
       parameter is substituted.

Thanks to everyone who participated in the above; I've appropriately added +1's throughout the thread!

Upvotes: 274

Archil Elizbarashvili
Archil Elizbarashvili

Reputation: 29

expr $str : '.*\(.\)'

Or

echo ${str: -1}

Upvotes: 1

phep
phep

Reputation: 581

Every answer so far implies the word "shell" in the question equates to Bash.

This is how one could do that in a standard Bourne shell:

printf "%s" "$str" | tail -c 1

Upvotes: 23

spiralofhope
spiralofhope

Reputation: 134

For anyone interested in a pure POSIX method:

https://github.com/spiralofhope/shell-random/blob/master/live/sh/scripts/string-fetch-last-character.sh

#!/usr/bin/env  sh

string_fetch_last_character() {
  length_of_string=${#string}
  last_character="$string"
  i=1
  until [ $i -eq "$length_of_string" ]; do
    last_character="${last_character#?}"
    i=$(( i + 1 ))
  done

  printf  '%s'  "$last_character"
}


string_fetch_last_character  "$string"

Upvotes: 0

urznow
urznow

Reputation: 1801

For portability you can say "${s#"${s%?}"}":

#!/bin/sh
m=bzzzM n=bzzzN
for s in \
    'vv'  'w'   ''    'uu  ' ' uu ' '  uu' / \
    'ab?' 'a?b' '?ab' 'ab??' 'a??b' '??ab' / \
    'cd#' 'c#d' '#cd' 'cd##' 'c##d' '##cd' / \
    'ef%' 'e%f' '%ef' 'ef%%' 'e%%f' '%%ef' / \
    'gh*' 'g*h' '*gh' 'gh**' 'g**h' '**gh' / \
    'ij"' 'i"j' '"ij' "ij'"  "i'j"  "'ij"  / \
    'kl{' 'k{l' '{kl' 'kl{}' 'k{}l' '{}kl' / \
    'mn$' 'm$n' '$mn' 'mn$$' 'm$$n' '$$mn' /
do  case $s in
    (/) printf '\n' ;;
    (*) printf '.%s. ' "${s#"${s%?}"}" ;;
    esac
done

Output:

.v. .w. .. . . . . .u. 
.?. .b. .b. .?. .b. .b. 
.#. .d. .d. .#. .d. .d. 
.%. .f. .f. .%. .f. .f. 
.*. .h. .h. .*. .h. .h. 
.". .j. .j. .'. .j. .j. 
.{. .l. .l. .}. .l. .l. 
.$. .n. .n. .$. .n. .n. 

Upvotes: 4

perreal
perreal

Reputation: 97948

That's one of the reasons why you need to quote your variables:

echo "${str:$i:1}"

Otherwise, bash expands the variable and in this case does globbing before printing out. It is also better to quote the parameter to the script (in case you have a matching filename):

sh lash_ch.sh 'abcde*'

Also see the order of expansions in the bash reference manual. Variables are expanded before the filename expansion.

To get the last character you should just use -1 as the index since the negative indices count from the end of the string:

echo "${str: -1}"

The space after the colon (:) is REQUIRED.

This approach will not work without the space.

Upvotes: 155

mark_infinite
mark_infinite

Reputation: 383

Try:

"${str:$((${#str}-1)):1}"

For e.g.:

someone@mypc:~$ str="A random string*"; echo "$str"
A random string*
someone@mypc:~$ echo "${str:$((${#str}-1)):1}"
*
someone@mypc:~$ echo "${str:$((${#str}-2)):1}"
g

Upvotes: 4

user5394399
user5394399

Reputation: 631

I know this is a very old thread, but no one mentioned which to me is the cleanest answer:

echo -n $str | tail -c 1

Note the -n is just so the echo doesn't include a newline at the end.

Upvotes: 63

Eduardo Cuomo
Eduardo Cuomo

Reputation: 18937

Single line:

${str:${#str}-1:1}

Now:

echo "${str:${#str}-1:1}"

Upvotes: 5

mr.baby123
mr.baby123

Reputation: 2312

another solution using awk script:

last 1 char:

echo $str | awk '{print substr($0,length,1)}'

last 5 chars:

echo $str | awk '{print substr($0,length-5,5)}'

Upvotes: 7

pradeep
pradeep

Reputation: 7

echo $str | cut -c $((${#str}))

is a good approach

Upvotes: -3

Related Questions