注意: 此问题的概要已经发布在PowerShell GitHub库中(点击此处),但后来被更全面的问题所取代。
在PowerShell中,传递给命令的参数是以参数模式解析的(与表达式模式相对应,请参阅
方便地,即使这些参数涉及变量引用(例如
注意:
• 我主要寻找所描述行为的设计原理或确认其是否为错误。如果这不是错误,则需要一些东西来帮助我概念化该行为,以便我能记住它并避免其缺陷。
• 所有这些边缘情况都可以通过显式双引号避免,鉴于上述非明显的行为,这可能是通常使用的最安全选择。
请注意,该段落没有说明当(非元字符)字符直接跟随以这些特殊字符开头的令牌时会发生什么,特别是
然而,该页面包含一个示例表明以变量引用开头的令牌也被解释为可扩展字符串:
如果这些有效:
为什么
这些微妙的区别很难记住,特别是在没有理由的情况下。
一个更有意义的规则是无条件地将以
请注意,以
作为旁注:这表明,在PowerShell中不通常支持类似于
但是,有一个例外:
在PowerShell中,传递给命令的参数是以参数模式解析的(与表达式模式相对应,请参阅
Get-Help about_Parsing
)。方便地,即使这些参数涉及变量引用(例如
$HOME\sub
)或子表达式(例如version=$($PsVersionTable.PsVersion)
),双引号括起来不包含空格或元字符的参数通常是可选的。
在大多数情况下,这样的未加引号的参数会被视为双引号字符串,并且适用于通常的string-interpolation rules(除了需要转义元字符如,
)。
我尝试总结了this answer中参数模式下未加引号标记的解析规则,但存在奇怪的边缘情况:
具体而言(截至Windows PowerShell v5.1),为什么以下每个命令中的未加引号的参数标记没有被识别为单个可展开的字符串,并导致传递了2个参数(变量引用/子表达式保留其类型)?
$(...)
at the start of a token:Write-Output $(Get-Date)/today # -> 2 arguments: [datetime] obj. and string '/today'
Note that the following work as expected:
Write-Output $HOME/sub
- simple var. reference at the startWrite-Output today/$(Get-Date)
- subexpression not at the start
.$
at the start of a token:Write-Output .$HOME # -> 2 arguments: string '.' and value of $HOME
Note that the following work as expected:
Write-Output /$HOME
- different initial char. preceding$
Write-Output .-$HOME
- initial.
not directly followed by$
Write-Output a.$HOME
-.
is not the initial char.
注意:
• 我主要寻找所描述行为的设计原理或确认其是否为错误。如果这不是错误,则需要一些东西来帮助我概念化该行为,以便我能记住它并避免其缺陷。
• 所有这些边缘情况都可以通过显式双引号避免,鉴于上述非明显的行为,这可能是通常使用的最安全选择。
可选阅读:文档状态和设计思考
截至本文撰写时,v5.1 Get-Help about_Parsing
页面:
不完全描述了规则
使用了一些术语,这些术语既没有在该主题中定义,也没有在 PowerShell 的世界中普遍使用(“可扩展字符串”,“值表达式” - 尽管人们可以猜测它们的含义)
从链接的页面中可以看到(重点加粗):
在参数模式下,每个值都被视为可扩展的字符串,除非它以以下特殊字符之一开头:美元符号($)、at 符号(@)、单引号(')、双引号(")或括号(()。如果前面带有这些字符之一,则该值将被视为值表达式。顺便提一下,以引号(")开头的标记当然也是可扩展的字符串(插值字符串)。关于引用的概念性帮助主题Get-Help about_Quoting_Rules
巧妙地避免了“扩展”和“插值”这两个术语。请注意,该段落没有说明当(非元字符)字符直接跟随以这些特殊字符开头的令牌时会发生什么,特别是
$
。然而,该页面包含一个示例表明以变量引用开头的令牌也被解释为可扩展字符串:
- 使用包含
4
的$a
,Write-Output $a/H
计算为(单个字符串参数)4/H
。
如果这些有效:
$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'
作为旁注:这表明,在PowerShell中不通常支持类似于
bash
的字符串连接——将任何混合引号和未引用标记放在一起——它仅在第一个子字符串/变量引用恰好是未引用的情况下才起作用。例如,Write-Output H/'2 + 2'
(与上面反转子字符串的示例不同)只生成一个参数。但是,有一个例外:
@
:当@
后面跟着一个语法上有效的变量名(例如@parms
)时,@
确实具有特殊含义(请参阅Get-Help about_Splatting
),但其他任何内容都会导致该标记被视为可扩展的字符串:Write-Output @parms # splatting (results in no arguments if $parms is undefined)
Write-Output @parms$a # *expandable string*: '@parms4', if $a equals 4
$var/string
的实际解析为一个可扩展字符串与文档所说的相反,因为表达式以$
开头。$(date)/sub
作为两个不同的参数(一个值表达式和一个裸字字符串)是完全有道理的。 - Mathias R. JessenHello'Hi'sup"whatup"
示例 - 我不知道它能用,但事实证明,只有在第一个子字符串是_unquoted_(尝试Write-Host -Object 'Hello'sup"whatup"
)时才有效,这正是引发这个问题的那种晦涩行为。 - mklement0$x=Get-Date; Write-Output $x/today
与相似的Write-Output $(Get-Date)/today
。 - JosefZ