Поделиться через


о_разборе

Краткое описание

Описывает, как PowerShell анализирует команды.

Длинное описание

При вводе команды в командной строке PowerShell разбивает текст команды на ряд сегментов, называемых маркерами , а затем определяет, как интерпретировать каждый маркер.

Например, если вы введите следующее:

Write-Host book

PowerShell разбивает команду на два маркера, Write-Host и book, и интерпретирует каждый маркер независимо с помощью одного из двух основных режимов синтаксического анализа: режима выражения и режима аргумента.

Заметка

При анализе входных данных команды PowerShell пытается разрешить имена команд команд в командлеты или собственные исполняемые файлы. Если имя команды не имеет точного совпадения, PowerShell добавляет Get- перед командой в качестве глагола по умолчанию. Например, PowerShell анализирует Service как Get-Service. Не рекомендуется использовать эту функцию по следующим причинам:

  • Это неэффективно. Это приводит к тому, что PowerShell будет выполнять поиск несколько раз.
  • Внешние программы с тем же именем разрешаются сначала, поэтому вы не можете выполнить предполагаемый командлет.
  • Get-Help и Get-Command не распознают имена без глаголов.
  • Имя команды может быть зарезервированным словом или ключевым словом языка. Process выполняет две функции, но не может быть применён к Get-Process.

Режим выражения

Режим выражения предназначен для объединения выражений, необходимых для обработки значений на языке скриптов. Выражения представляют собой представления значений в синтаксисе PowerShell и могут быть простыми или составными, например:

Литеральные выражения представляют собой прямые представления их значений:

'hello'
32

Выражения переменных содержат значение переменной, на которые они ссылаются:

$x
$Script:path

Операторы объединяют другие выражения для оценки:

-12
-not $Quiet
3 + 7
$input.Length -gt 1
  • строковые литералы символов должны содержаться в кавычках.
  • числа рассматриваются как числовые значения, а не как ряд символов (если не экранированы).
  • операторы , включая унарные операторы, такие как - и -not и двоичные операторы, такие как + и -gt, интерпретируются как операторы и применяют соответствующие операции к их аргументам (операнды).
  • выражения атрибута и преобразования анализируются как выражения и применяются к подчиненным выражениям. Например, [int] '7'.
  • Ссылки на переменные оцениваются по их значениям, но распаковка запрещена и вызывает синтаксическую ошибку.
  • Все остальное рассматривается как команда для вызова.

Режим аргумента

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

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

  • Знак доллара ($), за которым следует имя переменной, начинает ссылку на переменную, в противном случае она интерпретируется как часть расширяемой строки. Ссылка на переменную может включать доступ к членам или выполнение индексирования.

    • Дополнительные символы после простых ссылок на переменные, такие как $HOME, считаются частью одного и того же аргумента. Заключите имя переменной в фигурные скобки ({}), чтобы отделить ее от последующих символов. Например, ${HOME}.
    • Если ссылка на переменную включает доступ к члену, первый из дополнительных символов считается началом нового аргумента. Например, $HOME.Length-more дает два аргумента: значение $HOME.Length и строковый литерал -more.
  • Кавычки (' и ") начинают строки

  • Фигурные скобки ({}) начинают новый блок скрипта

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

  • Круглые скобки (()) начинают новое выражение

  • Оператор подвыражения ($()) начинает вложенное выражение

  • Инициал при знаке (@) начинает синтаксисы выражений, такие как splatting (@args), массивы (@(1,2,3)) и хэш-литералы таблицы (@{a=1;b=2}).

  • (), $()и @() в начале маркера создают новый контекст синтаксического анализа, который может содержать выражения или вложенные команды.

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

    • Метахарактеры в режиме аргумента (символы с особым синтаксическим значением) — <space> ' " ` , ; ( ) { } | & < > @ #. Из них < > @ # являются специальными только в начале токена.
  • Маркер остановки синтаксического анализа (--%) изменяет интерпретацию всех оставшихся аргументов. Дополнительные сведения см. в разделе маркер остановки анализа ниже.

Примеры

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

Пример Режим Результат
2 Выражение 2 (целое число)
`2 Выражение "2" (команда)
Write-Output 2 Выражение 2 (целое число)
2+2 Выражение 4 (целое число)
Write-Output 2+2 Аргумент "2+2" (строка)
Write-Output(2+2) Выражение 4 (целое число)
$a Выражение 4 (целое число)
Write-Output $a Выражение 4 (целое число)
$a+2 Выражение 6 (целое число)
Write-Output $a+2 Аргумент 4+2 (строка)
$- Аргумент $- (команда)
Write-Output $- Аргумент "$-" (строка)
a$a Выражение a$a (команда)
Write-Output a$a Аргумент "a4" (строка)
a'$a' Выражение a$a (команда)
Write-Output a'$a' Аргумент "a$a" (строка)
a"$a" Выражение a$a (команда)
Write-Output a"$a" Аргумент "a4" (строка)
a$(2) Выражение a$(2) (команда)
Write-Output a$(2) Аргумент "a2" (строка)

Каждый маркер можно интерпретировать как некоторый тип объекта, например, логический или строка. PowerShell пытается определить тип объекта из выражения. Тип объекта зависит от типа параметра, который ожидает команда, и от того, знает ли PowerShell, как преобразовать аргумент в правильный тип. В следующей таблице показаны несколько примеров типов, назначенных значениям, возвращаемым выражениями.

Пример Режим Результат
Write-Output !1 аргумент "!1" (строка)
Write-Output (!1) выражение Ложь (логическое)
Write-Output (2) выражение 2 (целое число)
Set-Variable AB A,B аргумент "A", "B" (массив)
CMD /CECHO A,B аргумент "A,B" (строка)
CMD /CECHO $AB выражение "A B" (массив)
CMD /CECHO :$AB аргумент ':A B' (строка)

Обработка специальных символов

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

"The value of `$ErrorActionPreference is '$ErrorActionPreference'."
The value of $ErrorActionPreference is 'Continue'.

Продолжение строки

Символ обратной кавычки можно также использовать в конце строки, чтобы продолжить ввод на следующей строке. Это улучшает удобочитаемость команды, которая принимает несколько параметров с длинными именами и значениями аргументов. Например:

New-AzVm `
    -ResourceGroupName "myResourceGroupVM" `
    -Name "myVM" `
    -Location "EastUS" `
    -VirtualNetworkName "myVnet" `
    -SubnetName "mySubnet" `
    -SecurityGroupName "myNetworkSecurityGroup" `
    -PublicIpAddressName "myPublicIpAddress" `
    -Credential $cred

Однако следует избегать использования продолжения строки.

  • Символы обратной кавычки могут быть трудно заметить и легко забыть.
  • Лишний пробел после бектека разрывает продолжение строки. Поскольку пространство трудно различить, может быть сложно найти ошибку.

PowerShell предоставляет несколько способов разрыва линий в естественных точках синтаксиса.

  • После символов вертикальной черты (|)
  • После двоичных операторов (+, -, -eqи т. д.)
  • После запятых (,) в массиве
  • После открытия таких символов, как [, {, (

Для большого набора параметров используйте вместо этого splatting. Например:

$parameters = @{
    ResourceGroupName = "myResourceGroupVM"
    Name = "myVM"
    Location = "EastUS"
    VirtualNetworkName = "myVnet"
    SubnetName = "mySubnet"
    SecurityGroupName = "myNetworkSecurityGroup"
    PublicIpAddressName = "myPublicIpAddress"
    Credential = $cred
}
New-AzVm @parameters

Передача аргументов в собственные команды

При выполнении собственных команд из PowerShell аргументы сначала анализируются PowerShell. Затем проанализированные аргументы объединяются в одну строку с каждым параметром, разделенным пробелом.

Например, следующая команда вызывает программу icacls.exe.

icacls X:\VMS /grant Dom\HVAdmin:(CI)(OI)F

Чтобы выполнить эту команду в PowerShell 2.0, необходимо использовать escape-символы, чтобы предотвратить неправильное понимание круглых скобок PowerShell.

icacls X:\VMS /grant Dom\HVAdmin:`(CI`)`(OI`)F

Маркер остановки синтаксического анализа

Начиная с PowerShell 3.0, можно использовать токен остановки анализа (--%), чтобы PowerShell не интерпретировал входные данные как команды или выражения PowerShell.

Заметка

Маркер остановки анализа предназначен только для использования встроенных команд на платформах Windows.

При вызове встроенной команды поместите маркер остановки анализа перед аргументами программы. Этот метод гораздо проще, чем использовать escape-символы, чтобы предотвратить неправильное понимание.

При обнаружении маркера остановки синтаксического анализа PowerShell обрабатывает оставшиеся символы в строке как литерал. Единственная интерпретация заключается в замене значений переменных среды, использующих стандартную нотацию Windows, например %USERPROFILE%.

icacls X:\VMS --% /grant Dom\HVAdmin:(CI)(OI)F

PowerShell отправляет следующую командную строку в программу icacls.exe:

X:\VMS /grant Dom\HVAdmin:(CI)(OI)F

Маркер остановки синтаксического анализа действует только до следующей новой строки или символа конвейера. Символ продолжения строки (`) нельзя использовать для расширения эффекта или использования разделителя команд (;) для прекращения его действия.

Кроме ссылок на переменные среды %variable%, вы не можете внедрить другие динамические элементы в команду. Экранирование символа % как %%, так, как это делается в пакетных файлах, не поддерживается. %<name>% токены неизменно расширяются. Если <name> не ссылается на определенную переменную среды, маркер передается через as-is.

Невозможно использовать перенаправление потоков (например, >file.txt), так как они передаются в качестве аргументов в целевую команду.

В следующем примере первый шаг выполняет команду без использования стоп-символа синтаксического анализа. PowerShell вычисляет строку с кавычками и передает значение (без кавычек) в cmd.exe, что приводит к ошибке.

PS> cmd /c echo "a|b"
'b' is not recognized as an internal or external command,
operable program or batch file.
PS> cmd /c --% echo "a|b"
"a|b"

Заметка

Маркер остановки синтаксического анализа не нужен при использовании командлетов PowerShell. Однако может быть полезно передать аргументы в функцию PowerShell, предназначенную для вызова собственной команды с этими аргументами.

Передача аргументов, содержащих символы кавычки

Некоторые собственные команды ожидают аргументы, содержащие символы кавычек. PowerShell 7.3 изменил способ анализа командной строки для собственных команд.

Осторожность

Новое поведение — это кардинальное изменение по сравнению с поведением Windows PowerShell 5.1. Это может нарушить сценарии и автоматизацию, которые обходят различные проблемы при вызове собственных приложений. Используйте маркер остановки синтаксического анализа (--%) или командлет Start-Process, чтобы избежать передачи аргументов по умолчанию при необходимости.

Новая переменная предпочтения $PSNativeCommandArgumentPassing управляет этим поведением. Эта переменная позволяет выбрать поведение во время выполнения. Допустимые значения: Legacy, Standardи Windows. Поведение по умолчанию зависит от платформы. На платформах Windows параметр по умолчанию — Windows, а на других платформах — Standard.

Legacy — это сложившееся исторически поведение. Поведение режимов Windows и Standard совпадает, но в режиме Windows вызовы следующих файлов автоматически используют передачу аргументов в стиле Legacy.

  • cmd.exe
  • cscript.exe
  • wscript.exe
  • заканчивается .bat
  • заканчивается .cmd
  • заканчивается .js
  • заканчивается .vbs
  • заканчивается .wsf

Если для $PSNativeCommandArgumentPassing задано значение Legacy или Standard, средство синтаксического анализа не проверяет наличие этих файлов.

Заметка

В следующих примерах используется средство TestExe.exe. Вы можете создать TestExe из исходного кода. См. TestExe в исходном репозитории PowerShell.

Новые возможности поведения, доступные этим изменением:

  • Литеральные или расширяемые строки со встроенными кавычками теперь сохраняются:

    PS> $a = 'a" "b'
    PS> TestExe -echoargs $a 'c" "d' e" "f
    Arg 0 is <a" "b>
    Arg 1 is <c" "d>
    Arg 2 is <e f>
    
  • Пустые строки в качестве аргументов теперь сохраняются:

    PS> TestExe -echoargs '' a b ''
    Arg 0 is <>
    Arg 1 is <a>
    Arg 2 is <b>
    Arg 3 is <>
    

Цель этих примеров — передать путь к каталогу (пробелы и кавычки) "C:\Program Files (x86)\Microsoft\" в собственную команду, чтобы он получил путь в виде строки с кавычками.

В режиме Windows или Standard следующие примеры приводят к ожидаемым результатам:

TestExe -echoargs """${Env:ProgramFiles(x86)}\Microsoft\"""
TestExe -echoargs '"C:\Program Files (x86)\Microsoft\"'

Чтобы получить те же результаты в режиме Legacy, необходимо экранировать кавычки или использовать маркер остановки синтаксического анализа (--%):

TestExe -echoargs """""${Env:ProgramFiles(x86)}\Microsoft\\"""""
TestExe -echoargs "\""C:\Program Files (x86)\Microsoft\\"""
TestExe -echoargs --% ""\""C:\Program Files (x86)\Microsoft\\"\"""
TestExe -echoargs --% """C:\Program Files (x86)\Microsoft\\""
TestExe -echoargs --% """%ProgramFiles(x86)%\Microsoft\\""

Заметка

Символ обратной косой черты (\) не распознается как escape-символ PowerShell. Это escape-символ, используемый базовым API для ProcessStartInfo.ArgumentList.

PowerShell 7.3 также добавил возможность трассировки привязки параметров для собственных команд. Дополнительные сведения см. в команд трассировки.

Передача аргументов в команды PowerShell

Начиная с PowerShell 3.0, вы можете использовать маркер окончания параметров (--), чтобы предотвратить интерпретацию входных данных как параметры PowerShell. Это соглашение, указанное в спецификации оболочки POSIX и служебных программ.

Маркер конца параметров

Маркер конца параметров (--) указывает, что все аргументы, следующие за ним, должны быть переданы в их фактической форме, как будто двойные кавычки были помещены вокруг них. Например, с помощью -- можно вывести строку -InputObject без использования кавычки или интерпретации в качестве параметра:

Write-Output -- -InputObject
-InputObject

В отличие от маркера остановки синтаксического анализа (--%) любые значения после маркера -- можно интерпретировать как выражения PowerShell.

Write-Output -- -InputObject $Env:PROCESSOR_ARCHITECTURE
-InputObject
AMD64

Это поведение применяется только к командам PowerShell. Если при вызове внешней команды используется маркер --, строка -- передается в качестве аргумента этой команде.

TestExe -echoargs -a -b -- -c

В выходных данных показано, что -- передается в качестве аргумента TestExe.

Arg 0 is <-a>
Arg 1 is <-b>
Arg 2 is <-->
Arg 3 is <-c>

Тильде (~)

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

PS D:\temp> $PWD

Path
----
D:\temp

PS D:\temp> Set-Location ~
PS C:\Users\user2> $PWD

Path
----
C:\Users\user2

В этом примере параметр Name параметра New-Item ожидает строку. Символ тильды рассматривается как буквальный символ. Чтобы изменить только что созданный каталог, необходимо указать путь с символом тильды.

PS D:\temp> Set-Location ~
PS C:\Users\user2> New-Item -Type Directory -Name ~

    Directory: C:\Users\user2

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d----            5/6/2024  2:08 PM                ~

PS C:\Users\user2> Set-Location ~
PS C:\Users\user2> Set-Location .\~
PS C:\Users\user2\~> $PWD

Path
----
C:\Users\user2\~

PowerShell 7.5-preview.2 вводит экспериментальную возможность расширения тильды до домашнего каталога пользователя для использования с собственными командами. Для получения дополнительной информации см. PSNativeWindowsTildeExpansion в разделе "Использование экспериментальных функций в PowerShell".

Развернутая строка передается во встроенную команду. Расширив тильду, PowerShell предотвращает ошибку для собственных команд в Windows, которые не поддерживают символ тильды. Вы можете увидеть результирующую строку, трассируя привязку параметров с использованием Trace-Command.

Trace-Command -Name ParameterBinding -Expression {
    findstr /C:\foo" ~\repocache.clixml
} -PSHost
DEBUG: 2024-05-06 15:13:46.8268 ParameterBinding Information: 0 : BIND NAMED native application line args [C:\Windows\system32\findstr.exe]
DEBUG: 2024-05-06 15:13:46.8270 ParameterBinding Information: 0 :     BIND cmd line arg [/C:\oo] to position [0]
DEBUG: 2024-05-06 15:13:46.8271 ParameterBinding Information: 0 :     BIND cmd line arg [C:\Users\user2\repocache.clixml] to position [1]
DEBUG: 2024-05-06 15:13:46.8322 ParameterBinding Information: 0 : CALLING BeginProcessing

Обратите внимание, что ~\repocache.clixml был расширен до C:\Users\user2\repocache.clixml.

См. также