PowerShell的解析模式:参数(命令)模式与表达模式

Bob*_*Bob 5 syntax powershell parsing

任何人都可以解释为什么当从powershell函数返回$ false时你无法使用比较运算符来确定函数是否返回$ false但是当你返回$ true时,比较评估为$ true?

function boolean {
    return $false
}

boolean -eq $false

function boolean {
    return $true
}

boolean -eq $true

>>>False
>>>True
Run Code Online (Sandbox Code Playgroud)

您可以通过将函数调用设置为变量来解决此问题,但我想知道是否有人可以解释这里发生的事情?

function boolean {
    return $false
}

$bool = boolean 
$bool -eq $false

function boolean {
    return $true
}

$bool = boolean
$bool -eq $true

>>>True
>>>True
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 6

PowerShell有两种基本的解析模式

  • 参数模式,类似于传统的 shell

    • 在参数模式下,第一个标记被解释为命令名称(例如cmdlet名称,函数名称或可执行文件的文件名),后跟由空格分隔参数列表
  • 表达模式,类似于传统的编程语言

运行Get-help about_Parsing提供了对这些模式的介绍。简而言之,它是确定应用哪种模式的第一个令牌
还要注意,给定的语句可能由以任何一种模式解析的部分组成。


boolean -eq $false参数模式下解析,因为它的第一个标记看起来像命令名称(标识符可以是程序名称,cmdlet名称,函数名称或别名)。

因此,-eq$false被解释为传递给function的参数(参数值)boolean

由于您boolean定义的函数不会强制将值仅传递给已声明的参数,因此有效地忽略了参数,并且语句的结果与函数输出($false$true)无关。

Mike Shepard的答案所示,您可以使函数强制使用仅声明的参数(包括none)以及带有param()装饰有该[CmdletBinding()]属性的块,如果您无意中将参数传递给了无参数boolean函数,则至少会导致错误。

您可以通过将函数调用设置为变量来解决此问题

$bool = boolean   # execute function and capture result in variable
$bool -eq $false  # use variable in the comparison 
Run Code Online (Sandbox Code Playgroud)

起作用的原因是该-eq语句$ -在这种情况下为变量引用- 开头,这使PowerShell在表达式模式下进行解析,该模式-eq被识别为运算符$false其RHS。

但是,不需要此中间步骤:

强制将一段代码解释为一个表达式,请将其包含在(...)

(boolean) -eq $false # Calls function 'boolean' and uses result as LHS of -eq
Run Code Online (Sandbox Code Playgroud)

(...)强制使用新的解析上下文(本身又以参数或表达式模式解析,再次取决于第一个标记),并将结果视为expression。然后,它可以将其用作较大表达式的一部分,例如-eq运算符的操作数或命令自变量


Mik*_*ard 5

PowerShell将-eq视为传递给"boolean"函数的参数的名称.

要看到这一点,您可以将函数调用放入parens中:

function boolean {
    return $false
}

(boolean) -eq $false

function boolean {
    return $true
}

(boolean) -eq $true
Run Code Online (Sandbox Code Playgroud)

或者,您可以将其设置为高级功能,以便在缺少参数(-eq)时出错:

function boolean {
[CmdletBinding()]
Param()
    return $false
}

boolean -eq $false

function boolean {
[CmdletBinding()]
Param()
    return $true
}

boolean -eq $true
Run Code Online (Sandbox Code Playgroud)