Como obter o último caractere de uma string em um shell?

125

Eu escrevi as seguintes linhas para obter o último caractere de uma string:

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

Funciona para abcd/:

$ bash last_ch.sh abcd/
/

Não funciona paraabcd* :

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

Ele lista os arquivos na pasta atual .

thinker3
fonte

Respostas:

95

Essa é uma das razões pelas quais você precisa citar suas variáveis:

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

Caso contrário, o bash expande a variável e, nesse caso, brilha antes de imprimir. Também é melhor citar o parâmetro para o script (caso você tenha um nome de arquivo correspondente):

sh lash_ch.sh 'abcde*'

Veja também a ordem das expansões no manual de referência do bash . As variáveis ​​são expandidas antes da expansão do nome do arquivo.

Para obter o último caractere, você deve apenas usar -1como índice, uma vez que os índices negativos contam desde o final da string:

echo "${str: -1}"

O espaço após os dois pontos ( :) é NECESSÁRIO.

Essa abordagem não funcionará sem o espaço.

perreal
fonte
62
BTW, índices negativos contam da direita, então "${1: -1}"é o suficiente.
choroba
7
Você deve responder como solução formal, não aqui nos comentários.
BMW
FYI: você também pode fazer isso em um "one-liner" como echo "${str:$((${#str}-1)):1}". Isso permite que você varie os caracteres finais retornados também. Este trabalho para mim no bash v4.1.0 (1)
SaxDaddy
16
@ Resposta do choroba: Observe o espaço maldito! Isso sempre tropeça me up: "${1:-1}não funciona "!
mxmlnkn
192

Por @perreal, citar variáveis ​​é importante, mas porque li este post 5 vezes antes de encontrar uma abordagem mais simples para a questão em questão nos comentários ...

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

Resultado: /

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

Resultado: *

Obrigado a todos que participaram disso acima; Adicionei + 1s apropriadamente em todo o segmento!

troca rápida
fonte
25
Nota: observe o espaço interno ${std: -1}, sem o espaço isso não funcionará. Eu me deparei com isso e descobri que ${std:~0}também funciona (com ou sem espaço). Não tenho certeza se esse é um comportamento documentado, porém, não me preocupei em procurá-lo.
Bart
4
está documentado. homem festa diz: As expressões aritméticas de partida com um - devem ser separados por espaços em branco a partir da anterior: a ser distinguido do uso valores padrão de expansão
mighq
3
Isso é incrível, obrigado por compartilhar. A página de manual do BASH é quase esmagadora de ler, eu já estive lá várias vezes. Eu acho que eles poderiam fazer um curso universitário sobre scripts de shell lol.
quickshiftin
3
Esta solução não funciona em um .shscript ao tentar atribuir o caractere recuperado a uma variável. Por exemplo, declare LAST=$(echo "${str: -1}") isso resultará na barra vermelha desagradável, mostrando um erro de sintaxe iniciando no:
krb686
3
Se a verificação de sintaxe editor não gosta que o espaço tentativa${str:0-1}
ttt
36

Eu sei que esse é um tópico muito antigo, mas ninguém mencionou qual é a resposta mais limpa para mim:

echo -n $str | tail -c 1

Observe que isso -né apenas para que o eco não inclua uma nova linha no final.

user5394399
fonte
15
Nem mesmo perto de ser mais limpo do que $ {str: -1}
shrewmouse
8
É mais limpo, pois não depende de um bash-ism, por isso funcionará com qualquer shell Posix.
John Eikenberry
Qual é o mais "bashist"? "$ {str: -1}" E ... Qual é o mais limpo? "$ {str: -1}" Bash é o besht! :-)
Roger
Para quem tenta imprimir os últimos caracteres em um arquivo grande, isso fez o trabalho para mim: cat user / folder / file.json | tail -c 1 Você pode substituir o 1 pelo número de caracteres que deseja imprimir.
Diego Serrano
5

outra solução usando o script awk:

último 1 caractere:

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

últimos 5 caracteres:

echo $str | awk '{print substr($0,length-5,5)}'
mr.baby123
fonte
1
Não é o que o OP pediu, mas esta é a melhor solução que encontrei para ser usada com um arquivo. A maioria das outras abordagens não funciona como um arquivo para ele, como em: cat file | awk '{print substr($0,length,1)}'Obrigado!
X28 de
4

Única linha:

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

Agora:

echo "${str:${#str}-1:1}"
Eduardo Cuomo
fonte
1
Por que você usa dois pares de parênteses? Além disso, desde o 4.2, você pode usar compensações negativas, conforme mostrado na resposta do quickshiftin: echo "${str: -1}"é suficiente (lembre-se do espaço entre :e -).
gniourf_gniourf
Dois pares de parênteses são usados para operações aritméticas
Eduardo Cuomo
Não. Consulte a parte relevante do manual onde você lerá: comprimento e deslocamento são expressões aritméticas (consulte Aritmética do Shell ); portanto, você já está com aritmética de casca e não precisa de nenhum parêntese. Além disso, se o que você disse fosse verdade, seria mais lógico ter uma expansão aritmética com $((...)).
gniourf_gniourf
@gniourf_gniourf você está certo! Agora eu entendo sua pergunta anterior. Resposta atualizada
Eduardo Cuomo
4

Até agora, todas as respostas implicam que a palavra "shell" na pergunta equivale a Bash.

É assim que se pode fazer isso em um shell Bourne padrão:

printf $str | tail -c 1
phep
fonte
1
echo -nconforme proposto por user5394399 evita problemas quando $strcontém caracteres de formato especial.
Conrad Meyer
A -nmudança é um basismo. Não funcionará no shell Bourne padrão como traço, etc ... Qual foi o ponto da minha resposta.
Phep # 25/18
1
Não é um basismo, mas o POSIX reconhece que sua implementação está definida ("Se o primeiro operando for -n, ou se algum dos operandos contiver um caractere <slash>, os resultados serão definidos pela implementação"). Sua resposta pode ser corrigida com printf "%s" "$str" | ...:-).
Conrad Meyer
4

Experimentar:

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

Por exemplo:

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
mark_infinite
fonte
-2
echo $str | cut -c $((${#str}))

é uma boa abordagem

pradeep
fonte