Pipeline functionality in PowerShell
PowerShell has the Pipeline structure support and that makes PowerShell more flexible, easy to use and easy to parse the output of the one command to the Next command as the Input.
Pipeline Structure:
As shown in the above diagram Command-A output is passed to the Command-B as the input. For example, Get-Service -Name Spooler command output is passed as the input to the Stop-Service.
Get-Service -Name Spooler | Stop-Service
Even a single command output in PowerShell uses a Pipeline. If we write the command only Get-Service, its output is also passed to the Pipelines as shown in the below code.
Get-Service | Out-Default | Out-Host
Pipeline Function:
So how does the actual Pipeline works in PowerShell? What exactly we can pass as input to the next command? We can’t pass any random things. For example, We can’t pass the process IDs to stop the services.
There are certain rules that the pipeline structure follows. The command which is accepting other commands output accepts two types of inputs.
- ValueFromPipeline
- ValueFromPipelineByPropertyName
ValueFromPipeline:
This property you can get using the Get-Help or Get-Command for the particular cmdlet. Consider the below example,
PS C:\> "Spooler" | Stop-Service -PassThru -Verbose
VERBOSE: Performing the operation "Stop-Service" on target "Print Spooler (Spooler)".
Status Name DisplayName
------ ---- -----------
Stopped Spooler Print Spooler
Here we are passing the Service name Spooler to the Stop-Service command. Let’s check the Stop-Service ValueFromPipeline property.
(Get-Command Stop-Service).ParameterSets.parameters
The above command retrieves the properties of all the parameters. We are just interested in the ValueFromPipeline Property. So we will filter it out.
(Get-Command Stop-Service).ParameterSets.parameters | where{$_.ValueFromPipeline -eq $true} | Select Name, ParameterType
Output:
Stop-Service accepts the two types of objects as input. One is InputObject and its Parameter type is ServiceController array. The second is Name and its parameter type is a String array.
We will first check the Name Property. It accepts the string array value of Service names.
PS C:\> "Spooler","Winrm" | Stop-Service -PassThru
Status Name DisplayName
------ ---- -----------
Stopped Spooler Print Spooler
Stopped Winrm Windows Remote Management (WS-Management)
Second is InputObject which accepts the ServiceController[]. To get the ServiceController type, we will use the Get-Service command and check its members using Get-Member.
You can see in the above image that Get-Service has the type ServiceController and it is for the single instance of the service, while the Stop-Service command accepts the array of the ServiceController means we can pass the multiple services. Ideally, the below example should work as per the above definition because we are passing an entire array of the ServiceController type which Stop-Service accepts.
Get-Service | Stop-Service
If you are unsure what kind of consequences would occur after running the command, use the -WhatIf parameter to get the command predicted operation as shown below.
Get-Service | Stop-Service -WhatIf
ValueFromPipelineByPropertyName:
Another useful property of the pipeline is ValueFromPipelineByPropertyName. To get which command parameters support the Pipeline Property, we will filter the command like as shown in the first property,
(Get-Command Stop-Service).ParameterSets.parameters | where{$_.ValueFromPipelineByPropertyName -eq $true} | Select Name, ParameterType
Output:
So the Stop-Service accepts Name Property (string array[]) as the input. Get-Service command which also has the Name property and that is the reason Stop-Service accepts the Get-Service Name property.
Example is shown below,
Get-Service -Name Spooler, Winrm | Stop-Service -PassThru
Let’s take another example : Stop-Process,
(Get-Command Stop-Process).ParameterSets.parameters | where{$_.ValueFromPipelineByPropertyName -eq $true} | Select Name, ParameterType
Output:
Stop-Process command accepts two ValueFromPipelineByPropertyName parameters. Name and ID and we can use Get-process commands which have both properties supported to retrieve the value and pass it to the next command.
Get-Process -Name notepad, powershell | Stop-Process -PassThru
Get-Process -ID 12234,3433 | Stop-Process -PassThru
This is how the above two properties are the cornerstone for the Pipeline Structure. We can create a small script from the above commands to include both properties and you can also create a module for the same, if you don’t want to execute the script every time and function should be readily available.
(Get-Command Stop-Process).ParameterSets.parameters | where{($_.ValueFromPipelineByPropertyName -eq $true) -or ($_.ValueFromPipeline -eq $true)} | Select Name, ParameterType
Output:
You can also create a function as shown below and store it to .psm1 file to create a module and store it to the PowerShell Profile path to available for other users.
function Check-PipeLineProperty{
param(
[Parameter(Mandatory=$true)]
[string[]]$commands
)
try{
foreach($C in $commands){
Write-Output "`n******************************************************************************************`n"
Write-Output "`n$c Pipeline Properties`n"
(Get-Command $C).ParameterSets.parameters | `
Where {($_.ValuefromPipeline -eq 'True') -or ($_.ValueFromPipelineByPropertyName -eq 'True')} | `
Select Name,ParameterType, IsMandatory,ValueFromPipeline,ValueFromPipelineByPropertyName | Sort-Object ValueFromPipeline -Descending | ft -AutoSize
}
}
catch{
Write-Host $_.Exception.Message -BackgroundColor DarkRed
}
}
Output:
Download the .psm1 module from the GitHub location below.