In PowerShell, syntax for if
is as so:
if (<test1>)
{<statement list 1>}
[elseif (<test2>)
{<statement list 2>}]
[else
{<statement list 3>}]
Another syntax rule is that for subexpressions, you need to use parentheses like this:
write-output (get-date)
So with these two rules combined, I would expect that the test for some path needs to be written with two sets of parentheses like this:
if ((Test-Path ...)) {
# do something
}
However, this also works:
if (Test-Path ...) {
# do something
}
and just for the sake of completeness, this doesn't work:
if (!Test-Path ...) {
# do something
}
(here, you would need to wrap the subexpression in parenthesis as usual).
Can anyone explain the syntax rules that apply here and how comes that I can use the IF test with one parenthesis only? Is it some PowerShell magic or am I misunderstanding the basic syntax rules?
Referring to C.2.2 from Appendix C: The PowerShell grammar in Bruce Payette's Windows PowerShell in Action, we have:
<ifStatementRule> =
'if' '(' <pipelineRule> ')' <statementBlockRule>
[ 'elseif' '(' <pipelineRule> ')' <statementBlockRule> ]*
[ 'else' <statementBlockRule> ]{0|1}
This indicates the (
and )
tokens as part of the literal syntax for recognizing an if
statement, and that the <test>
from the about_If
documentation refers to a pipeline that will be resolved to a Boolean.
Following the pipeline rules, we find:
Test-Path ...
parses to a <cmdletCall>
of <name> <parameterArgumentToken>
,!Test-Path ...
results in an <expressionRule>
of <UnaryOperatorToken> <propertyOrArrayReferenceRule>
, which fails when the cmdlet call cannot match the simple property or array rule, whereas!(Test-Path ...)
is able to match the parenthesized cmdlet call as a sub-expression.Edit: See also PowerShell 2.0 Language Specification (thanks to Roman's answer to another question).
The parentheses after the if
define a subexpression (if parentheses were required around Test-Path
, then we would need parens around $num -eq 5
and every other expression).. The additional parentheses after the not operator is required because Test-Path
needs to be evaluated before it can be negated. You can try this without an if statement.
This does not work:
PS> !Test-Path NonExistent.file
This does work:
PS> !(Test-Path NonExistent.file)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With