Skip to content

Latest commit

 

History

History

Statements-are-not-expressions

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

Statements are not expressions

PowerShell statements like if, for, switch, and etc. are not expressions and they normally cannot be used as expressions. But there is a special case. PowerShell 2.0 allowed to use them on assignment to a variable or a property.

For example, here is an alternative to the missing ternary operator (not ideal, see Missing-ternary-operator):

    $result = if ($condition) {$data1} else {$data2}

Ability to assign statement results is useful. But it also creates an illusion that statements are expressions and can be used as expressions in other cases.

For example, this works:

    $result = foreach($e in 1..5) {$e}
    $result | %{"Result is $_"}

As far as it works, it may look natural to eliminate the intermediate variable $result and just pipe (convert, redirect, etc.) results directly. But the following commands are not correct. Notice very different errors:

    # syntax error "An empty pipe element is not allowed."
    foreach($e in 1..5) {$e} | %{"Result is $_"}

    # syntax error "Unexpected token 'in' in expression or statement."
    [string](foreach($e in 1..5) {$e})

    # error "The term '>' is not recognized as the name of a cmdlet..."
    foreach($e in 1..5) {$e} > z.log

Note that the last command is not even a syntax error, it is the runtime error. The command works, it outputs numbers from 1 to 5 and then fails, see the test.

Workaround

The subexpression operator $() converts statements to an expression:

    $(foreach($e in 1..5) {$e}) | %{"Result is $_"}

    [string]$(foreach($e in 1..5) {$e})

    $(foreach($e in 1..5) {$e}) > z.log

The array subexpression operator @() also converts statements to an expression and guaranties that the result is an array:

    @(foreach($e in 1..5) {$e}).Count

Yet another way is to wrap statements with a script block and invoke it with operators . (in the current scope) or & (in this script block scope):

    .{foreach($e in 1..5) {$e}} | %{"Result is $_"}

    &{foreach($e in 1..5) {$e}} | %{"Result is $_"}

Scripts