En ZSH, ¿qué expresiones aritméticas pueden aparecer como subíndice de matriz?

Dec 22 2020

El manual de ZSH ( zshparam(1)) dice:

Array Subscripts
       Individual elements of an array may be selected using a subscript.  A
       subscript of the form `[exp]' selects the single element exp, where
       exp is an arithmetic expression which will be subject to arithmetic
       expansion as if it were surrounded by `$((...))'.

Sin embargo, esto falla rápidamente:

mc% arr=(a b c d e)     
mc% echo $arr[$#arr] e mc% echo $arr[$(($#arr))]
e
mc% echo $arr[$(($#arr - 1))] d mc% echo $arr[$#arr - 1]  
zsh: invalid subscript

Pregunta: ¿Por qué y cuáles son las excepciones?

Nota: esta pregunta proviene de la respuesta de don_crissti allí , donde sugieren $arr[RANDOM % $#arr + 1]acceder a un elemento aleatorio, pero esto arroja el error anterior.

Respuestas

5 Gilles'SO-stopbeingevil' Dec 23 2020 at 03:45

Técnicamente, cualquier expresión puede aparecer como subíndice. El problema es conseguir que el analizador ponga lo que quiere en el subíndice. Algunos personajes, incluidos los espacios, nunca lo logran. Solo los caracteres que constituyen la palabra pueden formar parte del subíndice, porque el subíndice es parte de una palabra.

mc% echo $arr[ 1] zsh: invalid subscript mc% echo $arr[1 ]
zsh: invalid subscript
mc% echo $arr[$#arr - 1]
zsh: invalid subscript
mc% echo $arr[$#arr-1]
d

El analizador de subíndice se detiene en el primer carácter no válido, y el error de "subíndice no válido" se activa antes de que zsh incluso compruebe el corchete de cierre final.

mc% echo $arr[ 1  
zsh: invalid subscript

En echo $arr[ 1], la parte posterior al espacio se considera de hecho una palabra separada: echorecibiría dos argumentos resultantes de la expansión de $arr[y 1], excepto que zsh no comienza a ejecutar ningún comando debido a la falla de análisis. Hay algunos casos en los que puede decir que lo que podría pensar como parte de la expresión aritmética no se analiza como tal, por ejemplo:

mc% echo $arr[1<<2]   
heredoc> << is a heredoc operator, not part of the subscript.
heredoc> 2]
zsh: invalid subscript

Por supuesto, los caracteres que no forman parte de una palabra pueden colarse como parte de una expansión anidada, como una expresión aritmética o una sustitución de comandos.

mc% echo $arr[$[1&3]] 
a
mc% echo $arr[`echo "1 + 2"`]  
c

Si la expansión del parámetro está entre comillas dobles, cualquier carácter (aparte de un corchete de cierre equilibrado o una comilla final) pasa a formar parte del subíndice. Eso es porque entre comillas dobles, cualquier carácter es efectivamente un carácter constituyente de una palabra. Del mismo modo, si la expansión del parámetro usa llaves, zsh busca la llave de cierre }para la expansión del parámetro antes de buscar el corchete de cierre ]para el subíndice, por lo que los caracteres que no forman parte de la palabra entran en el subíndice.

mc% echo "$arr[$#arr - 1]" d mc% echo ${arr[$#arr - 1]}
d

Si desea profundizar en los detalles esenciales, la función relevante es parse_subscript, llamada desde getindex.