Примечание. Сводка этого вопроса с тех пор была размещена в репозитории 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