powershellpipeline

In PowerShell, what's the difference between the operators "( )" and "|", such as `Write-Output (Get-Process)` and `Get-Process | Write-Output`?


I'm a novice in PowerShell.

Recently I encountered a problem about how to cascade commands or functions. I've found 2 ways:

But, what's the difference between the above 2 ways?

For example, the results of Write-Output (Get-Process) and Get-Process | Write-Output look the same. Are there different details hidden?

I've searched the doc about grouping-operator () and Pipeline operator |, and find:

Piping grouped expressions

When used as the first segment of a pipeline, wrapping a command or expression in parentheses invariably causes enumeration of the expression result. If the parentheses wrap a command, it's run to completion with all output collected in memory before the results are sent through the pipeline.

Grouping an expression before piping also ensures that subsequent object-by-object processing can't interfere with the enumeration the command uses to produce its output.

But I don't quite understand it. Maybe it's because I lack the basic knowledge of pipelines.

Thanks in advance.


Solution

  • Start from the base that Write-Output (Get-Process) is the same as storing the output from Get-Process in a variable and passing it as argument to Write-Output. When you use (...) the expression must be evaluated first, before the subsequent command receives that output, this can mean for example higher memory consumption.

    # can be considered the same as `Write-Output (Get-Process)`
    $tmp = Get-Process
    Write-Output $tmp
    $tmp = $null
    

    With the pipeline instead what you have is One-at-a-time processing, the subsequent command receives and processes one item at a time as soon as the preceding in pipeline starts outputting.

    This comparison between (...) and pipelines is very easy to see if you add a bit of delay between each object outputted:

    function Test-ProcessingFunction {
        param(
            [Parameter(ValueFromPipeline)]
            [int[]] $int
        )
    
        process {
            foreach ($i in $int) {
                "Processing $i"
            }
        }
    }
    
    $rangeDelayed = {
        0..10 | ForEach-Object { Start-Sleep -Milliseconds 200; $_ }
    }
    
    # nothing happens until `$rangeDelayed` finishes
    Test-ProcessingFunction (& $rangeDelayed)
    # as soon as `$rangeDelayed` outputs, it gets processed
    & $rangeDelayed | Test-ProcessingFunction