Оболочка POSIX: `$` теряет свое особое значение, если это последний символ в слове?

17

На пепел, тире и баш, когда я бегу

$ echo ab$

это возвращается

ab$

Это поведение определяется POSIX или это просто общее соглашение в POSIX-совместимых оболочках? Я не смог найти ничего на странице POSIX Shell Command Language, где упоминается такое поведение.

Гарольд Фишер
источник
4
Лучший вопрос: $ приобретает ли особое значение, если это последний символ в слове? Там нет единого специального значения, назначенного $; он используется для введения нескольких, но различных расширений, таких как расширение параметров ${...}, подстановка команд $(...)и арифметические выражения $((...)). В некоторых оболочках вводятся дополнительные контексты, такие как kshвариант подстановки команд x=${ echo foo; echo bar;}(который отличается от стандартного $(...)тем, что не выполняет команды в подоболочке).
Chepner
@chepner Не могли бы вы взвесить разницу во мнениях Иссака и Майкла Гомера? Их ответы явно противоречат друг другу
Гарольд Фишер
1
Я согласен с интерпретацией Майкла Гомера; оболочка даже не начинает беспокоиться о расширениях до тех пор, пока не будет завершен синтаксический анализ, поэтому в одном слове ab$нет символа, следующего за $ним, «следовал» ли он за нулевым символом в исходной входной строке или пробелом в регистре как echo ab$ foo; Оригинальный пробел без кавычек был распознан и отброшен после анализа.
Chepner

Ответы:

10

$сам по себе не имеет специального значения (try echo $), только в сочетании с другим символом после него и формированием расширения, например, $var(или ${var}) $(util),, $((1+2)).

$Получает свой «особенный» , что означает , как определение расширения в стандарте POSIX в разделе Токен распознавания :

Если текущий символ не заключен в кавычки $или `, оболочка должна идентифицировать начало любых кандидатов на расширение параметров, замену команд или арифметическое расширение из их вводных последовательностей символов без кавычек: $или ${, $(или `, и $((, соответственно. Оболочка должна прочитать достаточное количество входных данных, чтобы определить конец расширяемой единицы ( как объяснено в цитируемых разделах). Во время обработки символов, если экземпляры расширений или цитирования оказываются вложенными в подстановку, оболочка должна рекурсивно обрабатывать их способом, указанным для найденной конструкции. Символы, найденные от начала подстановки до ее конца, допускающие любую рекурсию, необходимую для распознавания встроенных конструкций, должны быть включены в маркер результата без изменений, включая любые встроенные или включающие операторы подстановки или кавычки. Маркер не должен быть ограничен к концу замены.

Итак, если $расширение не формируется, вступают в силу другие правила синтаксического анализа:

Если предыдущий символ был частью слова, текущий символ должен быть добавлен к этому слову.

Это охватывает вашу ab$строку.

В случае одинокого $(«новое слово» будет $само собой):

Текущий символ используется в качестве начала нового слова.

Значение генерируемого слова содержащего , $который не является стандартным расширением явно определяется как определенно с помощью POSIX.

Также обратите внимание, что $это последний символ $$, но это также и переменная, которая содержит PID текущей оболочки. В bash, !$может вызвать расширение истории (последний аргумент предыдущей команды). Таким образом, в общем, нет, $не без значения в конце слова без кавычек, но в конце слова это, по крайней мере, не означает стандартное расширение.

Кусалананда
источник
7

В зависимости от конкретной ситуации, это либо явно не определено (так что реализации могут делать, как они будут), либо должно происходить так, как вы заметили. В вашем точном сценарии echo ab$, POSIX предписывает вывод «абы $» , что вы наблюдали и это не определенно . Краткое резюме всех различных случаев в конце.

Есть два элемента: сначала разбить на слова, а затем интерпретировать эти слова.


токенизации

Токенизация POSIX требует, чтобы a $не было началом действительного расширения параметра , подстановки команды или арифметической подстановки, чтобы считаться литеральной частью WORDсоздаваемого токена. Это связано с тем, что правило 5 («Если текущий символ не заключен в кавычки $или `, оболочка должна идентифицировать начало любых кандидатов на расширение параметра, замену команды или арифметическое расширение из их вводных последовательностей символов без кавычек: $или ${, $(или `, и $((, соответственно» ) не применяется, поскольку ни одно из этих расширений не является жизнеспособным. Расширение параметра требует, чтобы там отображалось действительное имя, а пустое имя недопустимо.

Поскольку это правило не применимо, мы продолжаем следовать, пока не найдем то, которое применимо. Два кандидата - № 8 («Если предыдущий символ был частью слова, текущий символ должен быть добавлен к этому слову.») И № 10 («Текущий символ используется в качестве начала нового слова».) , которые применяются к echo a$и echo $соответственно.

Существует также третий случай формы, echo a$+bкоторая проходит через ту же трещину, поскольку +это не имя специального параметра. К этому мы вернемся позже, так как он запускает разные части правил.

Таким образом, спецификация требует, чтобы $синтаксически она считалась частью слова, и затем она может быть дополнительно обработана.


Расширение слова

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

Указано, что :

Если после символа «без кавычек» следует символ, который не является одним из следующих:

  • Числовой символ
  • Имя одного из специальных параметров (см. Специальные параметры )
  • Допустимый первый символ имени переменной
  • A <left-curly-bracket>('{')
  • <left-parenthesis>

результат не уточняется.

«Не указано» - это конкретный термин, означающий, что

  1. Соответствующая оболочка может выбрать любое поведение в этом случае
  2. Соответствующее приложение не может полагаться на какое-либо конкретное поведение

В вашем примере echo ab$, то $ не следует любому символ , так что это правило не применяется , и неопределенный результат не вызывается. Расширение $не вызывается, поэтому оно буквально присутствует и распечатывается.

Там , где она будет применяться в нашем третьем случае сверху: echo a$+b. Здесь $следует +, что не является число, специальный параметр ( @, *, #, ?, -, $, !, или 0), начало переменного имени (подчеркивания или буквенным от портативного набора символов ), или один из кронштейнов. В этом случае поведение не определено: соответствующая оболочка может изобрести специальный параметр, называемый +раскрытием , и соответствующее приложение не должно предполагать, что оболочка этого не делает . Оболочка может делать все что угодно, в том числе сообщать об ошибке.

Например, zsh, в том числе в своем режиме POSIX, интерпретируется $+bкак « bнабор переменных » и заменяет 1 или 0 вместо него. Он также имеет расширения для ~и =. Это соответствующее поведение.

Это может произойти и в другом месте echo "a$ b". Опять же, оболочке разрешено делать, как она хочет, и вы, как автор сценария, должны избегать $буквального вывода. Если нет, это может сработать, но вы не можете на это полагаться. Это абсолютная буква спецификации, но я не думаю, что такого рода детализация была задумана или рассмотрена.


В итоге

  • echo ab$: буквальный вывод, полностью указан
  • echo a$ b: буквальный вывод, полностью указан
  • echo a$ b$: буквальный вывод, полностью указан
  • echo a$b: расширение параметра b, полностью указано
  • echo a$-b: расширение специального параметра -, полностью указано
  • echo a$+b: неопределенное поведение
  • echo "a$ b": неопределенное поведение

В $конце слова вам разрешается полагаться на поведение, и оно должно рассматриваться буквально и передаваться echoкоманде как часть его аргумента. Это требование соответствия на оболочке.

Майкл Гомер
источник
Комментарии не для расширенного обсуждения; этот разговор был перенесен в чат .
Тердон
@MichaelHomer echo $Также будет буквальным и полностью указан?
Гарольд Фишер
@HaroldFischer Да
Майкл Гомер
Куда делись echo "$"и echo "a b$"падают?
Гарольд Фишер