W ZSH, jakie wyrażenia arytmetyczne mogą pojawić się jako indeks tablicy?

Dec 22 2020

Instrukcja ZSH ( zshparam(1)) brzmi:

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

Jednak szybko się to nie udaje:

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

Pytanie: Dlaczego i jakie są wyjątki?

Uwaga: to pytanie wywodzi się z odpowiedzi don_crissti tam , gdzie sugerują $arr[RANDOM % $#arr + 1]dostęp do losowego elementu, ale powoduje to powyższy błąd.

Odpowiedzi

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

Z technicznego punktu widzenia każde wyrażenie może pojawić się jako indeks dolny. Problem polega na tym, że parser umieści to, co chcesz, w indeksie dolnym. Niektórym postaciom, w tym spacjom, nigdy się to nie udaje. Tylko znaki składowe słowa mogą być częścią indeksu dolnego, ponieważ indeks dolny jest częścią słowa.

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

Parser indeksu dolnego zatrzymuje się na pierwszym nieprawidłowym znaku i wyzwalany jest błąd „nieprawidłowego indeksu”, zanim zsh nawet sprawdzi kończący nawias zamykający.

mc% echo $arr[ 1  
zsh: invalid subscript

W echo $arr[ 1]programie część po spacji jest faktycznie traktowana jako osobne słowo: echootrzymywałaby dwa argumenty wynikające z rozwinięcia $arr[i 1], z tym że zsh nie rozpoczyna wykonywania żadnego polecenia z powodu błędu parsowania. Jest kilka przypadków, w których możesz stwierdzić, że to, o czym możesz pomyśleć jako część wyrażenia arytmetycznego, w rzeczywistości nie jest analizowane jako takie, na przykład:

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

Znaki niebędące składnikami słowa mogą oczywiście wkraść się jako część zagnieżdżonej interpretacji, takiej jak wyrażenie arytmetyczne lub podstawienie polecenia.

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

Jeśli interpretacja parametrów znajduje się w cudzysłowach, każdy znak (poza zrównoważonym nawiasem zamykającym lub końcowym cudzysłowem) przechodzi jako część indeksu dolnego. Dzieje się tak, ponieważ wewnątrz podwójnych cudzysłowów każdy znak jest w rzeczywistości znakiem składowym słowa. Podobnie, jeśli interpretacja parametrów używa nawiasów klamrowych, zsh szuka nawiasu zamykającego }dla interpretacji parametrów, zanim szuka nawiasu zamykającego ]dla indeksu dolnego, a więc znaki niebędące składnikami słowa trafiają do indeksu dolnego.

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

Jeśli chcesz zagłębić się w szczegóły, odpowiednia funkcja jest parse_subscriptwywoływana z getindex.