Search notes:

Powershell: Pipelines

Contrary to other shells, PowerShell pipes objects rather than text from one command to another.

The power of pipelines

The power of pipelines can best be demonstrated with a simple example:
PS:\> get-service | where-object displayName -match '^SQL Server' | set-service -startupType manual
The first cmdLet (get-service) produces a set (actually an array) of objects that each represents a Windows service. (Technically, the objects in the array have the .NET type System.ServiceProcess.ServiceController).
The | symbol indicates that these objects should be passed (aka piped) to the next cmdLet, rather than printed to the screen.
The next cmdLet is where-object. It applies a required condition to the objects it receives, in our case, that the value of displayName (which is a member of the ServiceController object) matches the regular expression ^SQL Server.
Object that satisfy this condition are (most probably) services that are related to SQL Server.
The cmdLet discards objects that do not satisfy this condition.
Because there is yet another | symbol in the pipeline, PowerShell pipes the remaining object to the next cmdLet, set-service, to change the services' startup type to manual.
Such pipelines are powerful because they allow to work on a set of objects (such as services) simultaneously. Thus, they reduce human error and save time because tasks are automatable.

Referring to objects in a pipeline

A step in a pipeline can explicitly refer to the object it has received from its precedent step using the automatic variable $psItem for which $_ is an alias:
get-childItem -attr !d | forEach-object { "Size of file $($_.Name) is $($_.Length)"}

Last cmdlet

PowerShell automatically pipes the objects of the last cmdLet of a pipeline into the out-default cmdlet.
out-default will render the objects it receives according their format definitions.
By default, out-default invokes out-host for a stream of strings.

Pipeline parameter binding

A (non-beginning) command within a pipeline needs to know which parameter will get the object from the previous command.
This is determined in a process called pipeline parameter binding.
There are two means:

Removing objects from the pipeline

where-object cmdLet allows to specify a condition that determines which objects are kept in the pipeline.
Object that don't satisfy the specified conditions are discarded from the pipeline, that is, they are not passed to the next cmdLet in the pipeline.

Grep strings in the output of another string

See out-string -stream

One at a time vs All objects at once

All object at once

get-process returns an array of objects. Thus, the following statement prints the members of an array:
PS C:\> get-member -inputObject (get-process)

   TypeName: System.Diagnostics.Process
   …

One object at a time

However, when the result of get-process is piped into another command, the command receives one object at a time. Thus, get-member in the following pipeline prints the members of a System.Diagnostic.Process, which is the type of the objects returned by get-process.
Because get-member eliminates duplicates, we only see each member only once.
PS C:\> get-member -inputObject (get-process)

   TypeName: System.Object[]
   …

Multiple pipelines

Technically, PowerShell has mutliple pipelines:

Output of functions is written to pipelines

All output of functions is written to the pipeline.
This is fundamentally different from class methods whose output is not written to the pipeline. In order to write a value to the pipeline from a class method, the return statement is required.

Experimental features

PowerShell Core uses experimental features to play with the possibility to chain pipeline invocations with the pipeline chain operators && and ||.
This featues can be enabled with
PS C:\> enable-experimentalFeature PSPipelineChainOperators

Error message: An empty pipe element is not allowed

The following simple (read: silly) pipeline will cause an An empty pipe element is not allowed error message:
foreach ($i in 1 .. 10) {
   "line $i"
}  |  foreach-object {$_}
This is because some PowerShell statements cannot be used to pass objects down a pipeline:
If such statements are put into a & { … } or an $( … ) block, their output can be piped further down the pipeline:
& {
  foreach ($i in 1 .. 10) {
     "line $i"
  }
} | foreach-object {$_}

See also

The $_ automatic variable.
Formatting the output of a pipeline
Within a pipeline, a script block can be executed for each element in the pipeline with the forEach-object cmdLet.
If a function participates in a pipeline, the function can use the $input automatic variable to refer to the object that is being proessed in a process block.
The functionality of a pipeline ssems to be encapsulated in the .NET class System.Management.Automation.Runspaces.Pipeline.
The [parameter(valueFromPipeline=$true)] and [parameter(valueFromPipelineByPropertyName=$true)] parameter attributes.
The time it takes to finish a pipeline can be measued with the measure-command cmdlet
The write-output cmdlet can be used to explicitly pass one or more objects down to the next command in the primary pipeline.
A terminating error immediately terminates the execution of the current pipeline.

Index