¿Cómo obtener el último carácter de una cadena en un shell?

125

He escrito las siguientes líneas para obtener el último carácter de una cadena:

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

Funciona para abcd/:

$ bash last_ch.sh abcd/
/

No funciona paraabcd* :

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

Se enumeran los archivos de la carpeta actual .

pensador3
fuente

Respuestas:

97

Esa es una de las razones por las que necesita citar sus variables:

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

De lo contrario, bash expande la variable y en este caso hace globbing antes de imprimir. También es mejor citar el parámetro al script (en caso de que tenga un nombre de archivo coincidente):

sh lash_ch.sh 'abcde*'

Consulte también el orden de las expansiones en el manual de referencia de bash . Las variables se expanden antes de la expansión del nombre de archivo.

Para obtener el último carácter, debe usarlo -1como índice, ya que los índices negativos cuentan desde el final de la cadena:

echo "${str: -1}"

El espacio después de los dos puntos ( :) es REQUERIDO.

Este enfoque no funcionará sin el espacio.

perreal
fuente
62
Por cierto, los índices negativos cuentan desde la derecha, así que "${1: -1}"es suficiente.
choroba
7
Debe responder como una solución formal, no aquí en los comentarios.
BMW
FYI: también puede hacer esto en un "one-liner" como echo "${str:$((${#str}-1)):1}". Esto le permite también variar los caracteres finales devueltos. Esto funciona para mí en bash v4.1.0 (1)
SaxDaddy
16
La respuesta de @choroba: ¡Tenga en cuenta el maldito espacio! Esto siempre me "${1:-1}hace
tropezar
192

Por @perreal, citar variables es importante, pero como leí esta publicación como 5 veces antes de encontrar un enfoque más simple para la pregunta en cuestión en los comentarios ...

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

Salida: /

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

Salida: *

Gracias a todos los que participaron en esto arriba; ¡He agregado apropiadamente +1 en todo el hilo!

quickshiftin
fuente
25
NB: tenga en cuenta el espacio interior ${std: -1}, sin el espacio esto no funcionará. Me encontré con esto y descubrí que ${std:~0}también funciona (con o sin espacio). Sin embargo, no estoy seguro de si este es un comportamiento documentado, no me he molestado en buscarlo.
Bart
4
está documentado. man bash dice: Las expresiones aritméticas que comienzan con a - deben estar separadas por espacios en blanco de las anteriores: para distinguirse de la expansión Usar valores predeterminados
mighq
3
Eso es genial, gracias por compartir. Sin embargo, la página de manual de BASH es casi abrumadora de leer, he estado allí varias veces. Creo que podrían hacer un curso universitario sobre secuencias de comandos jajaja.
quickshiftin
3
Esta solución no funciona en un .shscript cuando se intenta asignar el carácter recuperado a una variable. Por ejemplo, declare LAST=$(echo "${str: -1}") esto dará como resultado la desagradable barra roja que muestra un error de sintaxis que comienza en:
krb686
3
si a la comprobación de sintaxis del editor no le gusta ese espacio, intente${str:0-1}
ttt
36

Sé que este es un hilo muy antiguo, pero nadie mencionó cuál para mí es la respuesta más limpia:

echo -n $str | tail -c 1

Tenga en cuenta que -nes solo para que el eco no incluya una nueva línea al final.

usuario5394399
fuente
15
Ni siquiera cerca de ser más limpio que $ {str: -1}
shrewmouse
8
Es más limpio ya que no depende de un bash-ismo, por lo que funcionará con cualquier shell de Posix.
John Eikenberry
¿Qué es lo más "basista"? "$ {str: -1}" Y ... ¿Qué es lo más limpio? "$ {str: -1}" ¡Bash es el mejor! :-)
Roger
Para cualquiera que intente imprimir los últimos caracteres en un archivo grande, esto hizo el trabajo por mí: cat user / folder / file.json | tail -c 1 Puede reemplazar el 1 con el número de caracteres que desea imprimir.
Diego Serrano
5

otra solución usando el script awk:

último 1 carácter:

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

últimos 5 caracteres:

echo $str | awk '{print substr($0,length-5,5)}'
mr.baby123
fuente
1
No es lo que pidió el OP, pero esta es la mejor solución que he encontrado para usar con un archivo. La mayoría de los otros enfoques no funcionarán con un archivo, como en: cat file | awk '{print substr($0,length,1)}'¡Gracias!
xmar
4

Linea sola:

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

Ahora:

echo "${str:${#str}-1:1}"
Eduardo Cuomo
fuente
1
¿Por qué usa dos pares de paréntesis? Además, desde 4.2, puede usar compensaciones negativas, como se muestra en la respuesta de quickshiftin: echo "${str: -1}"es suficiente (tenga en cuenta el espacio entre :y -).
gniourf_gniourf
Se utilizan dos pares de paréntesis para operaciones aritméticas
Eduardo Cuomo
No. Consulte la parte correspondiente del manual donde leerá: la longitud y el desplazamiento son expresiones aritméticas (consulte Aritmética de Shell ); por lo tanto, ya está en aritmética de shell y no necesita ningún paréntesis. Además, si lo que dijiste fuera cierto, sería más lógico tener una expansión aritmética con $((...)).
gniourf_gniourf
@gniourf_gniourf, ¡tienes razón! Ahora entiendo tu pregunta anterior. Respuesta actualizada
Eduardo Cuomo
4

Cada respuesta hasta ahora implica que la palabra "shell" en la pregunta equivale a Bash.

Así es como se podría hacer eso en un shell Bourne estándar:

printf $str | tail -c 1
Phep
fuente
1
echo -nsegún lo propuesto por user5394399 evita problemas cuando $strcontiene caracteres de formato especial.
Conrad Meyer
El -ncambio es un bashismo. No funcionará en el shell Bourne estándar como guión, etc., que fue el punto de mi respuesta.
phep
1
No es un bashismo, pero POSIX reconoce que está definido por la implementación ("Si el primer operando es -n, o si alguno de los operandos contiene un carácter <barra invertida>, los resultados están definidos por la implementación"). En cambio, su respuesta podría arreglarse con printf "%s" "$str" | ...:-).
Conrad Meyer
4

Tratar:

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

Por ejemplo:

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

es un buen enfoque

pradeep
fuente