How comes `if (Test-Path…)` actually works?

In PowerShell, syntax for if is as so:

if (<test1>)
    {<statement list 1>}
[elseif (<test2>)
    {<statement list 2>}]
    {<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)

Need Your Help

Sed/Awk manipulation of a text

linux text replace awk sed

I need to change the last column of a file:

Ray Trace Refraction looks “fake”

graphics 3d transparency raytracing aero-glass

So in my ray tracer that I'm building I've gotten refraction to work for spheres along with caustic effects, however the glass spheres don't look particularly good. I believe the refracted math is

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.