Объявление команды и расширения оболочки

Объявление команды и расширения оболочки

Я случайно наткнулся на следующее bashповедение, которое для меня оказалось неожиданным.

# The following works
$ declare bar=Hello                               # Line 1
$ declare -p bar                                  # Line 2
declare -- bar="Hello"
$ foo=bar                                         # Line 3
$ declare ${foo}=Bye                              # Line 4
$ declare -p bar                                  # Line 5
declare -- bar="Bye"
# The following fails, though
$ declare -a array=( A B C )                      # Line 6
$ declare -p array                                # Line 7
declare -a array=([0]="A" [1]="B" [2]="C")
$ foo=array                                       # Line 8
$ declare -a ${foo}=([0]="A" [1]="XXX" [2]="C")   # Line 9
bash: syntax error near unexpected token `('`
# Quoting the assignment fixes the problem
$ declare -a "${foo}=(A YYY C)"                   # Line 10
$ declare -p array                                # Line 11
declare -a array=([0]="A" [1]="YYY" [2]="C")

С момента расширения оболочки

  1. Расширение скобок
    • Расширение тильды
    • Расширение параметров и переменных
    • Арифметическое расширение
    • Процесс замены
    • Замена команды
  2. Разделение слов
  3. Расширение имени файла

выполняется в командной строке после ее разделения на токены (с последующим удалением кавычек), но до выполнения последней команды я бы не ожидал, что строка 9 завершится ошибкой.

В чем причина, по которой bashне принимается строка 9?Или, говоря по-другому,Что я упускаю из виду в способе обработки строки 9, из bash-за чего она терпит неудачу, но строка 10 выполняется успешно?

В любом случае, кавычки не всегда работают напрямую и потребуют особого внимания, если элементы массива представляют собой строки, например, с пробелами.

решение1

tl;dr; Я думаю, что это просто синтаксическая особенность, и не стоит предполагать за ней какой-то грандиозный замысел.

Bash использует сгенерированный bison/yaccпарсер, но, как и во многих других языках (C, Perl и т. д.), это не «чистый» парсер, но он также сохраняет некоторыесостояниеотдельно/параллельно грамматике в parser_stateпеременной.

Флаг, хранящийся в этой переменной состояния, — это PST_ASSIGNOK. Он будет установлен, когда некоторая встроенная функция, которая была проанализирована как WORDтокен, имела ASSIGNMENT_BUILTINв своих флагах.

Такими «встроенными функциями присваивания» являются local, typeset, declare, alias, exportи readonly.

Это PST_ASSIGNOKзаставит парсер считать скобки частью токена WORDпри использовании после назначения справа от такого встроенного. Но это НЕ изменит правила, которые определяют, является ли текущий токен на самом деленазначение: Поскольку ${foo}=(...)это недопустимое назначение, оно не будет проанализировано как одно слово, а скобки вызовут синтаксическую ошибку, как в echo foo(bar).

После того, как командная строка будет проанализирована, она будетрасширенный, и как часть расширений, любое составное присваивание ( WORDкоторое было отмечено как W_COMPASSIGN) как var=(1 2)будет выполнено и заменено на var, которое затем будет передано как аргумент встроенному как declare. Но если declare, после всех расширений, получает аргумент формы var=(...), он сам снова его проанализирует и расширит.

Итак, varname=foo; declare "$var=(1 2 3)"может быть похоже на declare foo='(1 2 3)'. Или на declare foo=(1 2 3), в зависимости от того, была ли уже определена переменная:

$ declare 'foo=(1 2 3)'; typeset -p foo
declare -- foo="(1 2 3)"
$ declare foo=(1); typeset -p foo
declare -a foo=([0]="1")
$ declare 'foo=(1 2 3)'; typeset -p foo
declare -a foo=([0]="1" [1]="2" [2]="3")

Я не думаю, что стоит полагаться на этот частный случай:

$ declare 'bar=(1 ( )'; typeset -p bar
declare -- bar="(1 ( )"
$ declare bar=(1); typeset -p bar
declare -a bar=([0]="1")
$ declare 'bar=(1 ( )'; typeset -p bar
bash: syntax error near unexpected token `('

Связанный контент