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

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.


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!


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.


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)}'

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}"

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

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

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.