¿Hay alguna manera de leer el último elemento de una matriz con bash?

68

Si tengo una matriz con 5 elementos, por ejemplo:

[a][b][c][d][e]

Usando echo ${myarray[4]}puedo ver lo que contiene.

Pero, ¿y si no supiera la cantidad de elementos en una matriz dada? ¿Hay alguna manera de leer el último elemento de una matriz de longitud desconocida? es decir, ¿el primer elemento que se lee de derecha a izquierda para cualquier matriz?

Me gustaría saber cómo hacer esto en bash.

3kstc
fuente
$@no es exactamente una matriz (no se puede suscribir). Para ello, consulte Cómo obtener el último argumento pasado a un script de shell .
Tom Hale

Respuestas:

89

Simplemente puede usar un índice negativo ${myarray[-1]} para obtener el último elemento. Puedes hacer lo mismo para el penúltimo, y así sucesivamente; en Bash:

Si el subíndice utilizado para hacer referencia a un elemento de una matriz indexada se evalúa en un número menor que cero, se interpreta como relativo a uno mayor que el índice máximo de la matriz, por lo que los índices negativos cuentan desde el final de la matriz, y un El índice de -1 se refiere al último elemento.

Lo mismo también funciona para la asignación. Cuando dice "expresión" realmente significa una expresión; puede escribir cualquier expresión aritmética allí para calcular el índice, incluida una que calcule utilizando la longitud de la matriz ${#myarray[@]}explícitamente.

Michael Homer
fuente
2
Puedes hacer eso en kshy zshtambién.
Janis
55
Sin zshembargo, por defecto, las matrices están indexadas en 1, a diferencia de bashy kshdonde están indexadas en 0.
Stephen Kitt
2
Sí, por supuesto; la respuesta corta a esta pregunta no cambia, pero como se mencionó la forma larga, pensé que era necesario señalar la diferencia en el comportamiento allí.
Stephen Kitt
22
El índice negativo solo funciona en bash 4.3 y superior.
cuonglm
10
La versión de Bash incluida con Mac OS X a partir de al menos v10.11.5 es solo 3.2, por lo que esto no funciona en Mac.
Doktor J
45

Bash moderno (v4.1 o mejor)

Puedes leer el último elemento en el índice -1:

$ a=(a b c d e f)
$ echo ${a[-1]}
f

El soporte para acceder a matrices indexadas numéricamente desde el final utilizando índices negativos comenzó con la versión bash 4.1-alpha .

Viejo bash (v4.0 o anterior)

Debe obtener la longitud de la matriz ${#a[@]}y luego restar uno para obtener el último elemento:

$ echo ${a[${#a[@]}-1]}
f

Como bash trata los subíndices de matriz como una expresión aritmética, no hay necesidad de notación adicional, como $((...))forzar la evaluación aritmética.

John1024
fuente
el último no me funciona; Estoy usando Bash v4.1.2 (1): en lugar de imprimir el último elemento, simplemente imprime toda la matriz.
Alexej Magura
Sin embargo, la respuesta de @ cuonglm funciona.
Alexej Magura
La respuesta sería aún mejor si pudieras calificar moderncon una versión.
Samveen
1
Exactamente lo que se necesitaba para que la respuesta fuera perfecta.
Samveen
1
Gracias por esto. Estaba usando echo $ {a [$ (($ {# a [@]} - 1]))} porque no sabía sobre "bash trata los subíndices de matriz como una expresión aritmética".
Bruno Bronosky
15

bashLa asignación de matriz, la referencia, el desarmado con índice negativo solo se agregaron en bash 4.3 . Con una versión anterior de bash, puede usar la expresión en el índicearray[${#array[@]-1}]

Otra forma, también funciona con una versión anterior de bash(bash 3.0 o mejor):

$ a=([a] [b] [c] [d] [e])
$ printf %s\\n "${a[@]:(-1)}"
[e]

o:

$ printf %s\\n "${a[@]: -1}"
[e]

El uso de desplazamiento negativo, es necesario separar :con -para evitar ser confundido con la :-expansión.

Cuonglm
fuente
1
Haga eso "${a[@]: -1}"y funcionará (además bashy zsh) también en ksh.
Janis
Los documentos de Kornshell ( www2.research.att.com/sw/download/man/man1/ksh.html ) lo especifican por completo. (No he inspeccionado los documentos de zsho bash; pero lo probé en los tres depósitos).
Janis
@ Janis: vuelva a leer la documentación de bash, también mencionó sobre este, también. Gracias de nuevo.
cuonglm
4

formación

Las alternativas más antiguas en bash (desde bash 3.0+) son:

$ a=(aa bb cc dd ee)
$ echo "${a[@]:(-1)}   ${a[@]: -1}   ${a[@]:(~0)}   ${a[@]:~0}"
ee   ee   ee   ee

El espacio es necesario para evitar la interpretación de :seguido de un signo menos -como la expansión de "${var:-abc}"(Usar valores predeterminados).

El ~es una negación aritmética de bits (equivalente al complemento de uno o voltear todos los bits ). De man bash:

EVALUACIÓN ARITMÉTICA

      ! ~         logical and bitwise negation  

Desde bash-4.2 + también:

$ echo "${a[-1]}   ${a[(~0)]}"
ee   ee

Desde bash 5.0+ también:

$ echo "${a[~0]}"
ee

Para todas las versiones de bash (bash más antiguo):

$ echo "${a[   ${#a[@]}-1   ]}"    # spaces added **only** for readability
ee

@ @

Para argumentos posicionales (desde bash 2.01):

$ set aa bb cc dd ee
$ echo "${@:(-1)} ${@:~0} ${@: -1} ${@:$#}   ${!#}"
ee ee ee   ee

Una solución portátil para todos los shells es usar eval:

eval printf '"%s\n"' \"\${$#}\"
Isaac
fuente
¿Tiene una referencia para la sintaxis bash 5+? He buscado en los 58 casos de ~en el manual y no lo vi.
Tom Hale
... y cómo lo haces $@? bash: ${@[@]:(-1)}: bad substitution
Tom Hale
1
Sí, hay una man bashreferencia (verifique la respuesta ampliada en el título @). @TomHale
Isaac
1
The no@ es una matriz (bueno, no es completamente una matriz ) en bash y no acepta el subíndice index ( ) para argumentos individuales. Necesitas usar o equivalente. Verifique la entrada expandida en el título. @TomHale[]${@:(-1)}@
Isaac
-2

También puedes hacer esto:

$ a=(a b c d e f)
$ echo ${a[$(expr ${#a[@]} - 1)]}

Resultado:

$ f

Lo que está haciendo es obtener todo el recuento de elementos en la matriz y restar -1 porque está obteniendo todos los elementos, no comenzando desde el índice de la matriz que es 0.

Javier Salas
fuente