Какие арифметические выражения могут отображаться в ZSH как нижний индекс массива?

Dec 22 2020

Руководство ZSH ( zshparam(1)) гласит:

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

Однако это быстро выходит из строя:

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

Вопрос: Почему и какие бывают исключения?

Примечание: этот вопрос проистекает из ответа don_crissti там , где они предлагают $arr[RANDOM % $#arr + 1]получить доступ к случайному элементу, но это вызывает указанную выше ошибку.

Ответы

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

Технически любое выражение может отображаться как нижний индекс. Проблема в том, чтобы синтаксический анализатор поместил то, что вы хотите, в нижний индекс. Некоторые символы, включая пробелы, так и не попадают. Только символы, составляющие слово, могут быть частью подстрочного индекса, поскольку подстрочный индекс является частью слова.

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

Анализатор нижнего индекса останавливается на первом недопустимом символе, и ошибка «недопустимый нижний индекс» запускается до того, как zsh даже проверит завершающую закрывающую скобку.

mc% echo $arr[ 1  
zsh: invalid subscript

В echo $arr[ 1]части после пробела фактически считается отдельным словом: echoполучит два аргумента в результате раскрытия $arr[и 1], за исключением того, что zsh не начнет выполнение какой-либо команды из-за сбоя синтаксического анализа. Есть несколько случаев, когда вы можете сказать, что то, что вы думаете как часть арифметического выражения, на самом деле не анализируется как таковое, например:

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

Конечно, символы, не входящие в состав слова, могут проникнуть внутрь как часть вложенного раскрытия, такого как арифметическое выражение или подстановка команд.

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

Если расширение параметра заключено в двойные кавычки, любой символ (кроме сбалансированной закрывающей скобки или конечной кавычки) проходит как часть подстрочного индекса. Это потому, что внутри двойных кавычек любой символ фактически является символом, составляющим слово. Точно так же, если в раскрытии параметра используются фигурные скобки, zsh ищет закрывающую скобку }для расширения параметра, прежде чем искать закрывающую скобку ]для нижнего индекса, и поэтому символы, не входящие в состав слова, действительно попадают в нижний индекс.

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

Если вы хотите погрузиться в подробности, соответствующая функция вызывается parse_subscriptиз getindex.