Em ZSH, quais expressões aritméticas podem aparecer como subscrito de array?

Dec 22 2020

O manual ZSH ( zshparam(1)) diz:

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 `$((...))'.

No entanto, isso falha rapidamente:

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

Pergunta: Por que e quais são as exceções?

Nota: esta questão decorre da resposta de don_crissti lá , onde eles sugerem $arr[RANDOM % $#arr + 1]acessar um elemento aleatório, mas isso gera o erro acima.

Respostas

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

Tecnicamente, qualquer expressão pode aparecer como um subscrito. O problema é fazer com que o analisador coloque o que você deseja no subscrito. Alguns personagens, incluindo espaços, nunca chegam. Apenas os caracteres constituintes da palavra podem fazer parte do subscrito, porque o subscrito é parte de uma palavra.

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

O analisador de subscrito para no primeiro caractere inválido, e o erro “subscrito inválido” é disparado antes mesmo de zsh verificar o colchete de encerramento.

mc% echo $arr[ 1  
zsh: invalid subscript

Em echo $arr[ 1], a parte após o espaço é de fato considerada uma palavra separada: echoreceberia dois argumentos resultantes da expansão de $arr[e 1], exceto que zsh não inicia a execução de nenhum comando devido à falha de análise. Existem alguns casos em que você pode dizer que o que você pode pensar como parte da expressão aritmética na verdade não é analisado como tal, por exemplo:

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

Caracteres não constituintes de palavras podem, é claro, entrar furtivamente como parte de uma expansão aninhada, como uma expressão aritmética ou uma substituição de comando.

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

Se a expansão do parâmetro estiver entre aspas duplas, qualquer caractere (exceto um colchete de fechamento equilibrado ou uma aspa final) passa para fazer parte do subscrito. Isso ocorre porque, dentro das aspas duplas, qualquer caractere é efetivamente um caractere constituinte da palavra. Da mesma forma, se a expansão do parâmetro usa colchetes, zsh procura a chave de fechamento }para a expansão do parâmetro antes de procurar o colchete de fechamento ]para o subscrito e, assim, os caracteres não constituintes da palavra fazem parte do subscrito.

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

Se você quiser mergulhar nos detalhes essenciais, a função relevante é parse_subscript, chamada de getindex.