简短说明
介绍 PowerShell 如何分析命令。
详细说明
在命令提示符处输入命令时,PowerShell 会将命令文本分解为 令牌 的一系列段,然后确定如何解释每个令牌。
例如,如果键入:
Write-Host book
PowerShell 将命令分解为两个令牌,Write-Host 和 book,并使用两种主要分析模式之一(表达式模式和参数模式)独立解释每个令牌。
注释
当 PowerShell 分析命令输入时,它会尝试将命令名称解析为 cmdlet 或本机可执行文件。 如果命令名称没有完全匹配,PowerShell 会将命令前面 Get- 为默认谓词。 例如,PowerShell 将 Service 分析为 Get-Service。 出于以下原因,不建议使用此功能:
- 效率低下。 这会导致 PowerShell 多次搜索。
- 首先优先解析具有相同名称的外部程序,因此您可能无法执行预期的 cmdlet。
-
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 首先将输入解释为表达式。 但是,当遇到命令调用时,分析将继续处于参数模式。 如果有包含空格(如路径)的参数,则必须将这些参数值括在引号中。
参数模式旨在解析 shell 环境中命令的参数和参数值。 所有输入都被视为可扩展字符串,除非它使用以下语法之一:
美元符号 (
$) 后跟变量名称是变量引用的开头,否则将其解释为可扩展字符串的一部分。 变量引用可以包括成员访问或索引。- 遵循简单变量引用的其他字符(如
$HOME)被视为同一参数的一部分。 将变量名称括在大括号({})中,以将其与后续字符分开。 例如,${HOME}。 - 当变量引用包含成员访问权限时,任何其他字符中的第一个被视为新参数的开头。 例如,
$HOME.Length-more产生两个参数:$HOME.Length的值和字符串文本-more。
- 遵循简单变量引用的其他字符(如
引号(
'和")是字符串的开头大括号 (
{}) 是新脚本块的开头逗号(
,)引入了作为数组传递的列表,除非调用的命令是本机应用程序,在这种情况下,它们被解释为可扩展字符串的一部分。 不支持初始、连续或尾随逗号。括号 (
()) 开始一个新表达式子表达式运算符 (
$()) 开始嵌入表达式初始
@符号是表达式语法的开头,例如展开 (@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) |
表达式 | False(布尔值) |
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等) - 在数组中的逗号 (
,) 后面 - 在
[、{和(等开始字符后面
对于大型参数集,请改用展开。 例如:
$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 中运行此命令,必须使用转义字符来防止 PowerShell 错误解释括号。
icacls X:\VMS /grant Dom\HVAdmin:`(CI`)`(OI`)F
停止分析标记
从 PowerShell 3.0 开始,可以使用 停止分析(--%)令牌来阻止 PowerShell 将输入解释为 PowerShell 命令或表达式。
注释
停止分析令牌仅用于在 Windows 平台上使用原生命令。
调用本机命令时,将停止分析令牌置于程序参数之前。 此方法比使用转义字符来防止误解要容易得多。
遇到停止分析令牌时,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 cmdlet 时不需要使用停止解析标记。 但是,将参数传递给 PowerShell 函数可能很有用,该函数旨在使用这些参数调用本机命令。
传递包含引号字符的参数
某些本机命令需要包含引号字符的参数。 PowerShell 7.3 更改了命令行针对本机命令进行分析的方式。
谨慎
新行为是 Windows PowerShell 5.1 行为的 重大更改 。 这可能会中断通过调用本机应用程序来解决各种问题的脚本和自动化流程。 使用停止分析标记 (--%) 或 Start-Process cmdlet 可在需要时避开本机参数传递。
新的 $PSNativeCommandArgumentPassing 首选项变量控制此行为。 此变量允许你在运行时选择行为。 有效值为 Legacy、Standard和 Windows。 默认行为特定于平台。 在 Windows 平台上,默认设置为 Windows,非 Windows 平台默认为 Standard。
Legacy 是历史行为。
Windows 和 Standard 模式的行为是相同的,但在 Windows 模式下,以下文件的调用会自动使用 Legacy 样式参数传递。
cmd.execscript.exewscript.exe- 以
.bat结尾 - 以
.cmd结尾 - 以
.js结尾 - 以
.vbs结尾 - 以
.wsf结尾
如果 $PSNativeCommandArgumentPassing 设置为 Legacy 或 Standard,则分析器不会检查这些文件。
注释
以下示例使用 TestExe.exe 工具。 可以从源代码生成 TestExe。 请参阅 PowerShell 源存储库中的 TestExe。
此更改提供的新行为:
现在会保留带有嵌入引号的文本或可扩展字符串:
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\\""
注释
PowerShell 无法将反斜杠 (\) 字符识别为转义字符。 它是基础 API 用于 ProcessStartInfo.ArgumentList 的转义字符。
PowerShell 7.3 还添加了跟踪本机命令的参数绑定的功能。 有关更多信息,请参阅 Trace-Command。
将参数传递给 PowerShell 命令
从 PowerShell 3.0 开始,可以使用 参数结束令牌(--)来阻止 PowerShell 将输入解释为 PowerShell 参数。 这是 POSIX Shell 和实用工具规范中指定的约定。
参数结束标记
参数结束标记(--)表示,其后面的所有参数都将以实际形式传递,就像在它们周围放置了双引号一样。 例如,使用 -- 可以输出字符串 -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 命令一起使用时,波形符将扩展到用户的主目录。 如果在路径中的其他任何位置使用波形符,则会将其视为文本字符。
PS D:\temp> $PWD
Path
----
D:\temp
PS D:\temp> Set-Location ~
PS C:\Users\user2> $PWD
Path
----
C:\Users\user2
在此示例中,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 会将波形符作为文本字符传递。 在路径中使用波形符会导致 Windows 上不支持波形符的本机命令出错。
PS D:\temp> $PWD
Path
----
D:\temp
PS D:\temp> Get-Item ~\repocache.clixml
Directory: C:\Users\user2
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 4/29/2024 3:42 PM 88177 repocache.clixml
PS D:\temp> more.com ~\repocache.clixml
Cannot access file D:\temp\~\repocache.clixml