如何在Shell中获取字符串的最后一个字符?

186

我写了以下代码来获得字符串的最后一个字符:

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

它适用于abcd/

$ bash last_ch.sh abcd/
/

abcd*无效

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

列出当前文件夹中的文件

11个回答

256

按照 @perreal 的说法,引用变量很重要,但我在评论中找到了一个更简单的方法来回答这个问题,所以我读了五遍文章...

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

如评论中所指出的那样,您也可以使用${str:0-1}

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

注意${str: -1}中的额外空格是必需的,否则如果str为空或者为零时,${str:-1}会将1作为默认值。

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

感谢所有参与以上讨论的人;我已经适当地在整个帖子中添加了“加1”!


33
请注意${std: -1}中间有空格,如果没有空格就无法正常工作。我遇到这个问题后发现${std:~0}也可以使用(带或不带空格)。不确定这是否是官方文档记录的行为,我还没有去查阅。 - Bart
8
"it is documented." 可以翻译为“已有记录”。man bash 中写道:“以 - 开头的算术表达式必须与前面的 : 用空格分开,以便与使用默认值扩展区分开来。” - mighq
3
太棒了,谢谢分享。不过BASH man页几乎令人无法理解,我已经看了好几次了。我认为他们可以开设一个关于Shell脚本编写的大学课程哈哈。 - quickshiftin
4
当尝试将检索到的字符分配给变量时,在.sh脚本中使用此解决方案无效。例如, declare LAST=$(echo "${str: -1}")会导致出现令人讨厌的红色错误栏,指示从冒号开始的语法错误。 - krb686
5
如果编辑器的语法检查不喜欢那个空格,请尝试${str:0-1} - ttt
1
${str::-1}${str: -1} 的另一种语法形式。 - lucasvc

146

这就是为什么您需要引用变量的原因之一:

echo "${str:$i:1}"
否则,bash会在输出之前扩展变量,而且在这种情况下进行globbing。最好引用脚本的参数(以防有匹配的文件名):
sh lash_ch.sh 'abcde*'

请参阅Bash参考手册中的扩展顺序。文件名扩展之前会先扩展变量。

要获取最后一个字符,只需使用索引-1,因为负数索引从字符串末尾开始计数:

echo "${str: -1}"

冒号 (:) 后面的空格是必须要有的。

没有空格这个方法将无法生效。


64
顺便提一下,负索引是从右边开始计数的,因此 "${1: -1}" 就足够了。 - choroba
7
您应该以正式解决方案回复,而不是在评论中回复。 - BMW
FYI:你也可以用“一行代码”来实现,如 echo "${str:$((${#str}-1)):1}"。这样可以返回不同数量的尾随字符。在我的 bash v4.1.0(1) 中有效。 - SaxDaddy
20
请注意该死的空格!这总是让我犯错:"${1:-1}"不起作用! - mxmlnkn

56

我知道这是一个非常老的主题,但没有人提到对我来说最干净的答案是什么:

echo -n $str | tail -c 1

注意-n只是为了让echo命令不在结尾添加换行符。


17
比${str: -1}干净还差得远。 - shrewmouse
16
由于它不依赖于Bash语法,因此更加干净,可以在任何Posix shell上运行。 - John Eikenberry
什么是最“bashist”的?"${str: -1}" 那么,什么是最干净的?"${str: -1}" Bash是最好的! :-) - Roger
对于任何试图打印大文件中的最后字符的人,以下代码对我很有帮助:cat user/folder/file.json | tail -c 1您可以将数字“1”替换为您想要打印的字符数。 - Diego Serrano

21

到目前为止,每个答案都暗示问题中的“shell”一词等同于Bash。

以下是在标准Bourne shell中执行此操作的方法:

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

2
用户5394399提出的echo -n可以避免在$str包含特殊格式字符时出现问题。 - Conrad Meyer
2
“-n”开关是Bashism。在标准Bourne shell中,如dash等不起作用...这就是我的答案的重点所在。 - phep
5
虽然不是完全的Bash特性,但POSIX标准认为这是实现相关的(“如果第一个操作数是-n,或者任何操作数包含<backslash>字符,则结果是实现定义的”)。您可以改用 printf“%s”“$str”| ... 来修复您的答案 :-). - Conrad Meyer

7

另一种使用awk脚本的解决方案:

最后一个字符:

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

最后 5 个字符:

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

1
这不是 OP 所要求的,但这是我发现用于文件的最佳解决方案。大多数其他方法不能将文件 cat 到它中,例如: cat file | awk '{print substr($0,length,1)}' 谢谢! - xmar

5

单行:

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

现在:

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

1
为什么要使用两对括号?自4.2版本以来,您可以使用负偏移量,如quickshiftin的答案所示:echo "${str: -1}"就足够了(注意:-之间的空格)。 - gniourf_gniourf
两对括号用于算术运算。 - Eduardo Cuomo
不行。请参阅手册的相关部分,您会看到:_length_和_offset_是算术表达式(请参见Shell Arithmetic);因此,您已经处于shell算术中,根本不需要任何括号。此外,如果您所说的是真的,那么使用$((...))进行_算术扩展_更为合理。 - gniourf_gniourf
@gniourf_gniourf 你是对的!我现在明白了你之前的问题。回答已更新。 - Eduardo Cuomo

4

尝试:

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

例如:
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

2

对于可移植性,你可以使用"${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

输出:

.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. 

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

或者

echo ${str: -1}

0

之前的答案 https://dev59.com/92Mm5IYBdhLWcg3whfWe#64616928 也是 POSIX 标准,而且更短。 - Richard Tingstad

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接