Skip to content

Latest commit

 

History

History
80 lines (54 loc) · 2.62 KB

File metadata and controls

80 lines (54 loc) · 2.62 KB

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