Reputation: 13291
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
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
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
Reputation: 134
For anyone interested in a pure POSIX method:
#!/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
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
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
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
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
Reputation: 18937
Single line:
${str:${#str}-1:1}
Now:
echo "${str:${#str}-1:1}"
Upvotes: 5
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