Подтвердить что ты не робот

Некотируемые токены в режиме аргумента с использованием переменных ссылок и подвыражений: почему они иногда разделяются на несколько аргументов?

Примечание. Сводка этого вопроса с тех пор была размещена в репозитории PowerShell GitHub, где обсуждение продолжается.

Аргументы, переданные команде в PowerShell, анализируются в режиме аргумента (в отличие от режима выражения - см. Get-Help about_Parsing).

Удобно, (double-) аргументы цитирования, которые не содержат пробелов или метасимволов, обычно являются необязательными, даже если эти аргументы содержат ссылки на переменные (например, $HOME\sub) или подвыражения (например, version=$($PsVersionTable.PsVersion).

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

Я попытался обобщить правила синтаксического анализа для некотируемых токенов в режиме аргумента в этом ответе, но есть любопытные кромки края:

В частности (с Windows PowerShell v5.1), , почему токен без кавычек в каждой из следующих команд НЕ распознается как одна расширяемая строка и приводит к получению 2 аргументов ( с переменной ссылкой/подвыражением, сохраняющей свой тип)?

  • $(...) в начале токена:

    Write-Output $(Get-Date)/today # -> 2 arguments: [datetime] obj. and string '/today'
    
    • Обратите внимание, что работа выполняется следующим образом:

      • Write-Output $HOME/sub - простой var. ссылка в начале
      • Write-Output today/$(Get-Date) - подвыражение не в начале
  • .$ в начале токена:

    Write-Output .$HOME  # -> 2 arguments: string '.' and value of $HOME
    
    • Обратите внимание, что работа выполняется следующим образом:

      • Write-Output /$HOME - другой начальный char. предшествующий $
      • Write-Output .-$HOME - начальный ., за которым непосредственно не следует $
      • Write-Output a.$HOME - . не является начальным char.

В стороне: с PowerShell Core v6.0.0-alpha.15, a = после простого var. ссылка в начале токена также, похоже, разбивает токен на 2 аргумента, чего не происходит в Windows PowerShell v5.1; например, Write-Output $HOME=dir.

Примечание:

  • Я в первую очередь ищет обоснование дизайна для описанного поведения или, в зависимости от обстоятельств, подтверждение того, что это ошибка. Если это не ошибка, я хочу, чтобы что-то помогло мне концептуализировать поведение, поэтому я могу запомнить его и избежать его подводных камней.

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


Дополнительное чтение: состояние документации и дизайна

На момент написания этой статьи v5.1 Get-Help about_Parsing страница:

  • не полностью описывает правила

  • использует термины, которые не определены ни в теме, ни вообще широко используются в мире PowerShell ( "расширяемая строка", "выражение ценности" - хотя можно догадаться об их значении)

На связанной странице (выделено мной):

В режиме аргумента каждое значение обрабатывается как расширяемая строка , если он не начинается с одного из следующих специальных символов: знак доллара ( $), при знаке (@), одинарная кавычка ('), двойная кавычка (") или открывающая скобка (().

Если ему предшествует один из этих символов, значение рассматривается как выражение значение.

В стороне: токен, начинающийся с ", конечно, по определению, также расширяемая строка (интерполирующая строка).
Любопытно, что концептуальная справочная тема о цитировании Get-Help about_Quoting_Rules позволяет избежать как терминов "развернуть", так и "интерполировать".

Обратите внимание, что в проходе не указано, что происходит, когда (немета) символы непосредственно следуют за токеном, который начинается с этих специальных символов, особенно $.

Однако страница содержит пример , который показывает, что токен, начинающийся с ссылки на переменную, также интерпретируется как расширяемая строка:

  • С $a, содержащим 4, Write-Output $a/H оценивает (один строковый аргумент) 4/H.

Обратите внимание, что этот отрывок подразумевает, что переменные ссылки/подвыражения внутри некорректного токена (который не начинается со специального char.) расширяются, как если бы внутри строки с двумя кавычками ( "рассматривались как расширяемая строка" ).

Если это работает:

$a = 4
Write-Output $a/H         # -> '4/H'
Write-Output H/$a         # -> 'H/4'
Write-Output H/$(2 + 2)   # -> 'H/4'

почему бы не Write-Output $(2 + 2)/H развернуть до '4/H' тоже (вместо того, чтобы обрабатываться как 2 аргумента?
Почему подвыражение в начале обрабатывается иначе, чем ссылка на переменную?

Такие тонкие различия трудно запомнить, особенно в отсутствие оправдания.

Правило, имеющее для меня больше смысла, - это безусловное обращение с токеном, который начинается с $, и имеет дополнительные символы, следующие за ссылочной ссылкой/подвыражением в качестве расширяемой строки.
(В отличие от этого, имеет смысл для автономной переменной/подвыражения переменной сохранить свой тип, как это происходит сейчас.)


Обратите внимание, что случай токена, начинающийся с .$, разделяющегося на 2 аргумента, вообще не рассматривается в разделе справки.


Еще более необязательное чтение: после токена, который начинается с одного из других специальных символов с дополнительными символами.

Среди других специальных символов для запуска токенов следующие безоговорочно обрабатывают любые символы, которые следуют за концом конструкции как отдельный аргумент (что имеет смысл):
( ' "

Write-Output (2 + 2)/H   # -> 2 arguments: 4 and '/H'
Write-Output "2 + $a"/H  # -> 2 arguments: '2 + 4' and '/H', assuming $a equals 4
Write-Output '2 + 2'/H   # -> 2 arguments: '2 + 2' and '/H'

В стороне: это показывает, что конкатенация bash -style string - размещение любой комбинации цитированных и некотируемых токенов рядом друг с другом - обычно не поддерживается в PowerShell; он работает только в том случае, если 1-я подстрока/переменная ссылка не определена. Например, Write-Output H/'2 + 2', в отличие от приведенного выше примера подстроки, создает только один аргумент.

Исключение составляет @: в то время как @ имеет особый смысл (см. Get-Help about_Splatting) после чего следует только синтаксически допустимое имя переменной (например, @parms), все остальное заставляет маркер снова рассматриваться как расширяемая строка:

Write-Output @parms    # splatting (results in no arguments if $parms is undefined)

Write-Output @parms$a  # *expandable string*: '@parms4', if $a equals 4
4b9b3361