2

The following code doesn't work

$result = (
    while ($true) { 
        <# Generating long list of psobject #> 
    }) | Tee-Object -FilePath 'fname.csv' | Group-Object -Property xxx | Select Name,Count

The term 'while' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.

ca9163d9
  • 27,283
  • 64
  • 210
  • 413
  • i cannot think of any reason why it SHOULD work in that situation. what are you actually attempting to achieve? – Lee_Dailey Oct 21 '19 at 15:21
  • @Lee_Dailey: Yes, [it should work](https://github.com/PowerShell/PowerShell/issues/6817). – mklement0 Oct 21 '19 at 16:10
  • @mklement0 - thank you for the info [*grin*] ... i disagree that it should work _in that situation_, but i can see that you - and others - would view it differently. i have enjoyed reading the workaround in your answer. – Lee_Dailey Oct 21 '19 at 16:25
  • i see the use of an expression around the `while` to be ... a bizarre, forced weirdness. it aint at all obvious and will be problematic when it comes to long term maintenance. that is not what the structure seems to be designed for. the more appropriate way would be to determine WHY the OP chose such an awkward structure - and then see if there are more obvious - and therefore more maintainable - solutions. – Lee_Dailey Oct 21 '19 at 16:51
  • @Lee_Dailey: The idea is not to use an expression _around_ the `while` loop, the idea is to be able to use the `while` loop _itself_ as an expression, _consistently_; you can already do it in a (non-pipeline) assignment, but you can't in a pipeline - there is no reason for this inconsistency, and it is the currently required workaround that is awkward and obscure. As for the _why_ : it should be possible to send _any_ expression's or statement's output to the pipeline, without having to jump through hoops. Pipeline input doesn't always have to come from _cmdlets_. – mklement0 Oct 21 '19 at 17:12
  • `$result = $(` will fix the parsing error, although an assignment normally has no output. – js2010 Aug 27 '20 at 16:53

1 Answers1

3

While:

  • you can use expressions as the first segment of a pipeline (e.g.,
    1..2 | Tee-Object -Variable result),

  • you cannot use compound statements (aka language statements) such as if, while, foreach, and do as-is, unfortunately.

That is, such compound statements aren't true expressions, even though in the context of assignments they can act a such. That is, you could do $result = while ($true) ... - without enclosing the while in (...) - yet you cannot send the while loop directly through a pipeline.

See GitHub issue #6817, which discusses this problematic "half-expression status" of compound statements, and asks if it's feasible to make them full expressions; as it turns out, the fundamentals of PowerShell's grammar prevent that.

Workarounds:

  • If you want your looping compound statement to stream, i.e. to output its objects one by one to the pipeline, as they become available - i.e. if you want the standard, streaming pipeline behavior:

    • Wrap your compound statement in & { ... } (or . { ... }, if you want to run directly in the current rather than in a child scope).
    • E.g., & { foreach ($i in 1..2) { $i } } | Tee-Object -Variable result
  • If you want to collect all outputs from your looping compound statement up front, before sending them through the pipeline:

    • Wrap your compound statement in $(...), the subexpression operator.
    • E.g., $(foreach ($i in 1..2) { $i }) | Tee-Object -Variable result
    • Note:
      • While using $(...) over & { ... } can speed up your pipeline, it does so only slightly, and it potentially comes at the expense of memory consumption, given that all outputs are invariably collected in memory first.
      • The same applies to @(...), the array-subexpression operator and (...), the grouping operator, but note that (...) only works with a single expression or command. This answer contrasts (...), $(...) and @(...) in detail.

Applied to your case:

$result = & {
    while ($true) { 
        <# Generating long list of psobject #> 
    } 
  } | Tee-Object -FilePath 'fname.csv' | ...
mklement0
  • 382,024
  • 64
  • 607
  • 775