Question:
In PowerShell, syntax for if
is as so:
1 2 3 4 5 6 7 |
if ( { [elseif ( { [else { |
Another syntax rule is that for subexpressions, you need to use parentheses like this:
1 2 |
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:
1 2 3 4 |
if ((Test-Path ...)) { # do something } |
However, this also works:
1 2 3 4 |
if (Test-Path ...) { # do something } |
and just for the sake of completeness, this doesn’t work:
1 2 3 4 |
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?
Answer:
Referring to C.2.2 from Appendix C: The PowerShell grammar in Bruce Payette’s Windows PowerShell in Action, we have:
1 2 3 4 5 |
'if' '(' [ 'elseif' '(' [ 'else' |
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).