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
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:
Or, you can make it an advanced function so you get an error with the missing parameter (-eq):
PowerShell has two fundamental parsing modes:
argument mode, which works like traditional shells
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 functionboolean
.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-lessboolean
function.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
(...)
:(...)
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.