Um comando para imprimir apenas os últimos 3 caracteres de uma string

28

Eu sei que o comando cut pode imprimir os primeiros n caracteres de uma string, mas como selecionar os últimos n caracteres?

Se eu tiver uma string com um número variável de caracteres, como posso imprimir apenas os três últimos caracteres da string. por exemplo.

"unlimited" output needed is "ted"
"987654" output needed is "654"
"123456789" output needed is "789"
    
por odyssey 22.10.2014 / 01:03

10 respostas

46

Por que ninguém deu a resposta óbvia?

sed 's/.*\(...\)//'

... ou o menos óbvio

grep -o '...$'

É certo que o segundo tem a desvantagem que linhas com menos de três caracteres desaparecem; mas a questão não definiu explicitamente o comportamento para este caso.

    
por 22.10.2014 / 01:51
36

Mantê-lo simples - cauda

Não devemos precisar de uma expressão regular ou mais de um processo apenas para contar caracteres.
O comando tail , geralmente usado para mostrar as últimas linhas de um arquivo, tem uma opção -c ( --bytes ), que parece ser a ferramenta certa para isso:

$ printf 123456789 | tail -c 3
789

(Quando você está em um shell, faz sentido usar um método como na resposta do mikeserv, porque ele salva o início do processo para tail .)

Caracteres Unicode reais?

Agora, você pergunta pelos três últimos caracteres ; Não é isso que esta resposta lhe dá: ela produz os três últimos bytes !

Desde que cada caractere seja um byte, tail -c apenas funciona. Por isso, pode ser usado se o conjunto de caracteres for ASCII , ISO 8859-1 ou uma variante.

Se você tiver uma entrada em Unicode, como no formato comum UTF-8 , o resultado está errado:

$ printf 123αβγ | tail -c 3
�γ

Neste exemplo, usando UTF-8 , os caracteres grego alfa, beta e gama têm dois bytes de comprimento:

$ printf 123αβγ | wc -c  
9

A opção -m pode pelo menos contar os caracteres reais do unicode:

printf 123αβγ | wc -m
6

Ok, os últimos 6 bytes nos fornecerão os 3 últimos caracteres:

$ printf 123αβγ | tail -c 6
αβγ

Portanto, tail não suporta o tratamento de caracteres gerais e nem mesmo tenta (veja abaixo): Ele lida com linhas de tamanho variável, mas sem caracteres de tamanho variável.

Vamos colocar desta forma: tail é o ideal para a estrutura do problema a ser resolvido, mas errado para o tipo de dados.

GNU coreutils

Olhando mais adiante, verifica-se que o GNU coreutils, a coleção de ferramentas básicas como sed , ls , tail e cut , ainda não está totalmente internacionalizada. Qual é principalmente sobre o suporte a Unicode.
Por exemplo, cut seria um bom candidato para usar em vez de cauda aqui para suporte de caracteres; Ele tem opções para trabalhar em bytes ou caracteres, -c ( --bytes ) e -m ( --chars );

Só que -m / --chars é, a partir da versão
cut (GNU coreutils) 8.21 , 2013,
não implementado!

De info cut :

'-c CHARACTER-LIST'
'--characters=CHARACTER-LIST'
     Select for printing only the characters in positions listed in CHARACTER-LIST.  
     The same as '-b' for now, but internationalization will change that.


Veja também esta resposta para .

    
por 22.10.2014 / 03:15
33

Se o seu texto estiver em uma variável de ambiente chamada STRING , você poderá fazer isso em um bash shell:

echo "${STRING:(-3)}"
    
por 22.10.2014 / 01:08
12

Usando awk :

awk '{ print substr( $0, length($0) - 2, length($0) ) }' file
ted
654
789
    
por 22.10.2014 / 01:07
10

Se a string estiver em uma variável, você pode fazer:

printf %s\n "${var#"${var%???}"}"

Isso remove os três últimos caracteres do valor de $var , como:

${var%???}

... e depois retira da parte de $var de tudo mas o que acabou de ser removido:

${var#"${var%???}"}

Este método tem suas vantagens e desvantagens. No lado positivo, ele é totalmente POSIX-portátil e deve funcionar em qualquer shell moderno. Além disso, se $var não contiver pelo menos três caracteres nothing , mas o \n ewline final for impresso. Então, novamente, se você quiser impresso nesse caso, você precisa de uma etapa adicional como:

last3=${var#"${var%???}"}
printf %s\n "${last3:-$var}"

Dessa forma, $last3 estará sempre vazio se $var contiver 3 ou menos bytes. E $var só é substituído por $last3 se $last3 estiver vazio ou unset - e sabemos que não é unset porque apenas o definimos.

    
por 22.10.2014 / 01:12
7

Você pode fazer isso, mas isso é um pouco ... excessivo:

for s in unlimited 987654 123456789; do
    rev <<< $s | cut -c 1-3 | rev
done 
ted
654
789
    
por 22.10.2014 / 01:36
2

A solução à prova de balas para cordas utf-8:

utf8_str=$'\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82' # привет

last_three_chars=$(perl -CAO -e 'print substr($ARGV[0], -3)' "$utf8_str")

Ou use:

last_three_chars=$(perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' "$utf8_str")

para evitar o manuseio incorreto de dados.

Exemplo:

perl -MEncode -CO -e '
  print substr(decode("UTF-8", $ARGV[0], Encode::FB_CROAK), -3)
' $'\xd0\xd2\xc9\xd7\xc5\xd4' # koi8-r привет

Produz algo assim:

utf8 "\xD0" does not map to Unicode at /usr/lib/x86_64-linux-gnu/perl/5.20/Encode.pm line 175.

Não depende das configurações de localidade (por exemplo, funciona com LC_ALL=C ). Bash , sed , grep , awk , rev requer algo assim: LC_ALL=en_US.UTF-8

Solução comum:

  • Receber bytes
  • Detectar codificação
  • Decodificar bytes para caracteres
  • Extrair caçadores
  • Codificar caractere para bytes

Você pode detectar a codificação com uchardet . Veja também projetos relacionados .

Você pode decodificar / codificar com Codificar em Perl, codecs no Python 2.7

Exemplo :

Extraia os últimos três caracteres da string utf-16le e converta esses caracteres em utf-8

utf16_le_str=$'\xff\xfe\x3f\x04\x40\x04\x38\x04\x32\x04\x35\x04\x42\x04' # привет

chardet <<<"$utf16_le_str"  # outputs <stdin>: UTF-16LE with confidence 1.0

last_three_utf8_chars=$(perl -MEncode -e '
    my $chars = decode("utf-16le", $ARGV[0]);
    my $last_three_chars = substr($chars, -3);
    my $bytes = encode("utf-8", $last_three_chars);
    print $bytes;
  ' "$utf16_le_str"
)

Veja também: perlunitut , PDF Unicode HOWTO 2

    
por 10.07.2015 / 00:15
1

Que tal usar "expr" ou "rev"?

Uma resposta semelhante à fornecida por @ G-Man : expr "$yourstring" : '.*\(...\)$' Tem a mesma desvantagem que a solução grep.

Um truque bem conhecido é combinar "cut" com "rev": echo "$yourstring" | rev | cut -n 1-3 | rev

    
por 13.11.2015 / 16:22
0

Obtenha o tamanho da string com:

size=${#STRING}

Em seguida, obtenha substring do último caractere n:

echo ${STRING:size-n:size}

Por exemplo:

STRING=123456789
n=3
size=${#STRING}
echo ${STRING:size-n:size}

daria:

789
    
por 10.07.2015 / 13:13
0

printf não funcionará se a string tiver espaços.

Abaixo do código para string com espaço

str="Welcome to Linux"
echo -n $str | tail -c 3

nux

    
por 10.03.2018 / 08:12