Формулировки «контекст списка» и «контекст строки» пришли из Perl, но аналогичные концепции применимы к языку оболочки. Обратите внимание, что это похожие понятия :виды контекстов и последствия типа контекста различны.
Слово контекст является техническим термином в семантике языка программирования. Его точное значение привязано к конкретной семантической формализации, которая выходит за рамки данного ответа. Когнитивное значение — это природа окружения фрагмента кода. Например, заявление о том, что фрагмент кода $foo
имеет разное значение в разных контекстах, означает, что поведение программы, содержащей $foo
, зависит от природы того, что происходит вокруг этого вхождения $foo
в программе.
Семантика оболочки довольно сложна. Он не подпадает под традиционные категории, которые вы найдете во вводных учебниках по языкам программирования. Выполнение программы оболочки можно разбить на две фазы (обратите внимание, что это способ представить семантику,это не означает, что интерпретатор оболочки должен быть разбит таким образом):
Этап синтаксического анализа превращает строку (содержимого исходного файла или аргумента в-c
)в абстрактное синтаксическое дерево. В спецификации POSIX это соответствует шагам 2 и 3 (распознавание и разбор токена ). Спецификация POSIX определяет правила грамматики , которые описывают форму дерева. Обратите внимание, что это не контекстная -свободная грамматика – представление основано на обычном представлении контекстных -свободных грамматик, но аннотации «применить правило N » делают его более сложным математическим объект.
Этап выполнения выполняет некоторую оценку узлов дерева и вызывает внешние команды. В спецификации POSIX это соответствует шагам 4–7 (раскрытие, перенаправление, выполнение команды и ожидание ).
Расширение — это процесс, который применяется к определенному типу узла в абстрактном синтаксическом дереве, который POSIX называет WORD
и который обычно называют «словом». Его можно разделить на две группы.
Первая группа расширений состоит, в терминологии POSIX, из расширения с тильдой (, например. ~foo
→ /home/foo
), расширение параметра(напр. $foo
→ bar
, если значение foo
равно bar
), подстановка команды (, например. $(foo)
→ bar
, если выводом команды foo
являетсяbar
)и арифметическое расширение (, например. $((2+2))
→4
). Эта первая группа расширений выполняется для каждого слова, за исключением символов, которые «заключены в кавычки» в силу того, что они заключены в одинарные кавычки или им предшествует обратная косая черта. Результатом этой группы расширений является приблизительно строка с аннотациями (. Я объясню приближение ниже ).
Вторая группа расширений состоит из разделения полей и расширения пути (, широко известного как «генерация имени файла» или «подстановка» ). Эта группа расширений превращает аннотированную строку в список строк. Эта группа расширений выполняется на подмножестве мест, где выполняется первая группа :не выполняется на частях слов, заключенных в двойные кавычки, и вообще не выполняется, если слово в определенных позициях в абстрактном синтаксическом дереве . Именно здесь контексты списка и строки вступают в :в определенных контекстах, т.е. для определенных классов позиций в абстрактном синтаксическом дереве выполняется вторая группа расширений. Это контексты списка , названные так потому, что результатом процесса раскрытия является список (строк ). В других контекстах, называемых строковыми контекстами , вторая группа расширений не выполняется, и результатом процесса раскрытия является одна строка.
POSIX описывает удаление кавычек как последнюю стадию расширения. Это один из способов объяснить заключение в кавычки, когда все раскрытия перед разделением полей определяются как преобразование строки в строку. Например, для слова '$foo'$bar\$qux
при условии, что значение переменной bar
равно value
, расширение параметра превращает его в '$foo'value\$qux
, а другие первые -групповые расширения оставляют строку неизменной. Удаление кавычек, наконец, удаляет кавычки, чтобы получить $foovalue$qux
.
Представление с удалением кавычек требует выполнения сопоставления кавычек на каждом этапе. Презентация, которая проще для понимания и реализации и дает тот же конечный результат, заключается в выполнении этапа расшифровки кавычек, результатом которого является список частей. Каждая часть аннотируется, чтобы помнить, цитировалась ли она. Например, '$foo'$bar\$qux
заключает в кавычки следующие части :, заключает в кавычки $foo
, расширение переменной bar
без кавычек,цитируемый $
, голый q
, голый u
, голый x
. (Различие между «кавычками» и «голыми» необходимо для таких вещей, как определение присвоений и принятие решения о том, следует ли расширять псевдонимы. )Второй этап раскрытия -происходит только для частей без кавычек в контексте списка.
POSIX указывает, происходит ли вторая группа расширений, путем явного перечисления этапов расширения. Например, «Каждое назначение переменной должно быть расширено для расширения с помощью тильды, расширения параметра, подстановки команд, арифметического расширения и удаления кавычек перед присвоением значения». Более простой способ сформулировать это так: происходят только первые раскрытия группы -, т. е. это присваивание является строковым контекстом. Контекста всего два, потому что есть только два набора правил выполнения раскрытий :либо выполняется вся первая группа (контекст строки ), либо обе группы выполняются по порядку (контекст списка ).
(На самом деле, чтобы быть полным, существует третий вид контекста,:case
контекст шаблона. В шаблоне case
выполняются только раскрытия первой -группы (, как и в строковом контексте ), но часть второй группы расширений имеет значение — символы подстановки без кавычек являются подстановочными знаками для сопоставления строк.)
Определение языка определяет, какие контексты являются контекстами списка, а какие строковыми контекстами. В принципе, это может быть произвольно. Однако за этим стоит интуиция :в тех местах, где грамматика ожидает список из WORD
токенов, над этими токенами выполняется второе -групповое расширение, тогда как в местах, где грамматика требует одиночный WORD
, второй -Расширение группы не выполняется. Простой способ объяснить это так: там, где грамматика ожидает список, это контекст списка, а там, где грамматика ожидает одну строку, это строковый контекст.