Scripting Constructs and the Pipeline

Hi, folks. This is my first post, so please be kind. I’ll gladly take any constructive criticism!

I have researching this for the past couple of days and I think I understand how it works, but I’d like to see if you guys can confirm, and possibly explain more eloquently, what is going on here. I am doing this for my own edification as well as in hopes that other people who are trying to understand this can read this and get an increased level of understanding.

Will you guys please see if this understanding / description of the pipeline in the context of script blocks within constructs makes sense? Is there a better way to articulate this accurately?

Thanks in advance for any guidance you can provide to me.

This works:
$(if($True) {Get-ChildItem | Sort-Object -Property Name -Descending}) | Sort-Object -Property Name

This fails:
if($True) {Get-ChildItem | Sort-Object -Property Name -Descending} | Sort-Object -Property Name

Bottom line: A scriptblock contained within a scripting construct (such as “if” or “foreach”) cannot add anything to the “real” pipeline and expect it to live in the pipeline beyond the scope of that scripting construct. However, the contents of the “fake” pipeline can be captured in a variable or via a $() subexpression and then used as input to the “real” pipeline.

Think more in terms of “nested” pipelines and subexpressions (expressions in parentheses). Scripting constructs don’t have their own scope per se; anything in them does emit to the “main” pipeline. However, in your second example, that “main” pipeline isn’t what’s “feeding” your Sort-Object.

Very broadly speaking, it’s uncommon - if not a poor practice - to mix and match “scripting” and “one-liners” like this, in part because the data flow can get confusing pretty quickly. For example, if I rewrote your second example using more “proper” formatting:

if($True) {
  Get-ChildItem | Sort-Object -Property Name -Descending
| Sort-Object -Property Name

It’s perhaps more visually apparent that it’s an odd mashup that I wouldn’t expect to work. The end of the construct is the end - while something inside it has in fact emitted to the success pipeline, the last line is clearly starting with nothing being piped to Sort-Object.

You’re just playing around with what I guess I’d consider an edge case in terms of PowerShell’s “language.” I’d look sideways if I saw someone constructing something like this in a real, production-quality script.

Thanks so much for the explanation! I will gladly take your advice and avoid the mix 'n match approach.

For the sake of clarity, are you saying that the approach that works (using the subexpression) is also sub-optimal / poor form or is that technique legitimate?

It’s not a great approach. It doesn’t use a pipeline really well; it has to buffer all the output from the sub expression in memory until the expression completes. So it’s not memory efficient. It’s also hard to visually parse which means harder to maintain. Personally I’d never write it that way. Not sober.

Thanks, Don. Your explanation certainly makes sense. I’ll either take your advice or start drinking. I appreciate your help!