PowerShell's parsing modes: argument (command)

2019-02-15 06:23发布

Can anyone explain why when returning $false from a powershell function you are unable use a comparison operator to determine if the function has returned $false but when you return $true the comparison evaluates to $true?

function boolean {
    return $false
}

boolean -eq $false

function boolean {
    return $true
}

boolean -eq $true

>>>False
>>>True

You can work around this by setting the function call to a variable, but I was wondering if anyone could explain what is happening here under the hood?

function boolean {
    return $false
}

$bool = boolean 
$bool -eq $false

function boolean {
    return $true
}

$bool = boolean
$bool -eq $true

>>>True
>>>True

2条回答
Melony?
2楼-- · 2019-02-15 06:43

PowerShell is seeing -eq as the name of a parameter being passed to the "boolean" function.

To see this, you can put the function call in parens:

function boolean {
    return $false
}

(boolean) -eq $false

function boolean {
    return $true
}

(boolean) -eq $true

Or, you can make it an advanced function so you get an error with the missing parameter (-eq):

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

boolean -eq $false

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

boolean -eq $true
查看更多
叼着烟拽天下
3楼-- · 2019-02-15 06:57

PowerShell has two fundamental parsing modes:

  • argument mode, which works like traditional shells

    • In argument mode, the first token is interpreted as a command name (such as cmdlet name, function name, or filename of an executable), followed by a whitespace-separated list of arguments.
  • expression mode, which works like traditional programming languages.

Running Get-help about_Parsing provides an introduction to these modes; in short, it is the first token that determines which mode is applied.
Also note that a given statement may be composed of parts that are parsed in either mode.


boolean -eq $false is parsed in argument mode, because its first token looks like a command name (an identifier that could be a program name, cmdlet name, function name, or alias).

Therefore, -eq and $false are interpreted as arguments (parameter values) to pass to function boolean.

Since your boolean functions are defined in a way that doesn't enforce passing values only to declared parameters, the arguments are effectively ignored, and the result of the statement is whatever the functions output ($false or $true).

As demonstrated in Mike Shepard's answer, you can make a function enforce use of only declared parameters (including none) with a param() block decorated with the [CmdletBinding()] attribute, which would at least result in an error if you inadvertently passed arguments to the parameter-less boolean function.

You can work around this by setting the function call to a variable

$bool = boolean   # execute function and capture result in variable
$bool -eq $false  # use variable in the comparison 

The reason this works is that the -eq statement starts with $ - a variable reference in this case - which causes PowerShell to parse in expression mode, where -eq is recognized as an operator and $false as its RHS.

However, there is no need for this intermediate step:

To force a piece of code to be interpreted as an expression, enclose it in (...):

(boolean) -eq $false # Calls function 'boolean' and uses result as LHS of -eq

(...) forces a new parsing context (which in itself is parsed in either argument or expression mode, again depending on the 1st token) and treats the result as an expression. which then allows its use as part of a larger expression, such as as an operand of the -eq operator, or as a command argument.

查看更多
登录 后发表回答