Acessando os últimos x caracteres de uma string no Bash

124

Eu descobri que com ${string:0:3}um pode acessar os 3 primeiros caracteres de uma string. Existe um método equivalente fácil de acessar os três últimos caracteres?

aldorado
fonte

Respostas:

235

Últimos três caracteres de string:

${string: -3}

ou

${string:(-3)}

(observe o espaço entre :e -3na primeira forma).

Consulte a Expansão dos parâmetros do shell no manual de referência :

${parameter:offset}
${parameter:offset:length}

Expands to up to length characters of parameter starting at the character
specified by offset. If length is omitted, expands to the substring of parameter
starting at the character specified by offset. length and offset are arithmetic
expressions (see Shell Arithmetic). This is referred to as Substring Expansion.

If offset evaluates to a number less than zero, the value is used as an offset
from the end of the value of parameter. If length evaluates to a number less than
zero, and parameter is not ‘@’ and not an indexed or associative array, it is
interpreted as an offset from the end of the value of parameter rather than a
number of characters, and the expansion is the characters between the two
offsets. If parameter is ‘@’, the result is length positional parameters
beginning at offset. If parameter is an indexed array name subscripted by ‘@’ or
‘*’, the result is the length members of the array beginning with
${parameter[offset]}. A negative offset is taken relative to one greater than the
maximum index of the specified array. Substring expansion applied to an
associative array produces undefined results.

Note that a negative offset must be separated from the colon by at least one
space to avoid being confused with the ‘:-’ expansion. Substring indexing is
zero-based unless the positional parameters are used, in which case the indexing
starts at 1 by default. If offset is 0, and the positional parameters are used,
$@ is prefixed to the list.

Como esta resposta obtém algumas visões regulares, deixe-me acrescentar a possibilidade de abordar o comentário de John Rix ; como ele menciona, se a sua string tiver comprimento menor que 3, ela se ${string: -3}expandirá para a string vazia. Se, nesse caso, você desejar a expansão de string, poderá usar:

${string:${#string}<3?0:-3}

Isso usa o ?:operador ternário se, que pode ser usado na aritmética do shell ; como documentado, o deslocamento é uma expressão aritmética, isso é válido.

gniourf_gniourf
fonte
5
Observe que isso não funciona se sua string for menor que o deslocamento negativo que você fornecer. Você apenas obtém uma string vazia nesses casos.
precisa saber é o seguinte
2
@gniourf_gniourf Como usar isso junto com a substituição de comandos? Eu estou tentando extrair os três últimos caracteres do meu hostname deppfx@localhost:/tmp$ echo ${$(hostname): -3} -bash: ${$(hostname): -3}: bad substitution
deppfx
3
@deppfx: você não pode no Bash. Utilize uma variável temporária: temp=$(hostname); echo "${temp: -3}". O Bash também possui a HOSTNAMEvariável (que pode ou não diferir da saída de hostname). Se você quiser usá-lo, apenas faça echo "${HOSTNAME: -3}".
Gnourf_gniourf
Como você pode usar isso a partir da saída de um tubo? Por exemplo, some func | echo ${string: -3}- como atribuo a saída de some funcpara string?
Michael Hays
1
@ MichaelHays: Basta atribuir a saída da sua função: string=$(some func)e então echo "${string: -3}".
Gniourf_gniourf 13/05/19
48

Você pode usar tail:

$ foo="1234567890"
$ echo -n $foo | tail -c 3
890

Uma maneira um tanto indireta de obter os três últimos caracteres seria dizer:

echo $foo | rev | cut -c1-3 | rev
devnull
fonte
2
O "tail" também é útil no dash, quando o bash não está disponível. Por exemplo, seção de script inicial.
Vskubriev 29/03
13

Outra solução alternativa é usar grep -ocom um pouco de magia regex para obter três caracteres seguidos até o final da linha:

$ foo=1234567890
$ echo $foo | grep -o ...$
890

Para obter opcionalmente os 1 a 3 últimos caracteres, no caso de cadeias com menos de 3 caracteres, você pode usar egrepcom esta regex:

$ echo a | egrep -o '.{1,3}$'
a
$ echo ab | egrep -o '.{1,3}$'
ab
$ echo abc | egrep -o '.{1,3}$'
abc
$ echo abcd | egrep -o '.{1,3}$'
bcd

Você também pode usar intervalos diferentes, como 5,10os últimos cinco a dez caracteres.

Aurelio Jargas
fonte
7

Para generalizar a pergunta e a resposta de gniourf_gniourf (como era isso que eu estava procurando), se você deseja cortar um intervalo de caracteres de, digamos, 7 do final para 3 do final, você pode usar esta sintaxe:

${string: -7:4}

Onde 4 é a duração do curso (7-3).

Além disso, embora a solução de gniourf_gniourf seja obviamente a melhor e mais organizada, eu só queria adicionar uma solução alternativa usando o cut :

echo $string | cut -c $((${#string}-2))-$((${#string}))

Isso é mais legível se você fizer isso em duas linhas, definindo o comprimento $ {# string} como uma variável separada.

Adrian Tompkins
fonte
uma variante não mencionada na outra resposta é combinar essa cutabordagem de calcular o início / parada primeiro e, em seguida, apenas usar essas variáveis ​​na expansão de parâmetros (também vale mencionar que os cutdesvios bash e iniciam em 1 e zero, respectivamente, portanto, seria necessário para ser calculado nos cálculos, o que não estou fazendo aqui): start=$((${#string}-3)); stop=$((${#string}));e depois echo ${string: $start : $stop}vsecho $string | cut -c "$start"-"$stop"
Michael
(oh, não se preocupe - vejo que meu comentário não aborda o problema de a string ser muito curta e, em seguida, não causa nenhuma saída - tanto a expansão dos parâmetros de corte quanto de string. Ainda assim, dividi-la em variáveis ​​( sem necessariamente chamando comandos extras) faz com que seja mais fácil de ler e ainda seria uma boa idéia, eu acho).
michael