PowerShell Genie

PowerShell Pipeline binding

To make your function accept pipeline input, mark a parameter [Parameter(ValueFromPipeline)] and put your work in a process block.

Lesson 2 min read Windows PowerShell 5.1 and PowerShell 7

Documentation: Microsoft Learn

Overview

An advanced function can have three blocks. begin runs once before any input arrives, which is where you set up a counter or open a connection. process runs once per incoming object, and is where the actual work goes. end runs once after the last object, which is where you emit a summary.

The mistake that follows from not knowing this is putting pipeline work outside process. Code in the function body without a process block runs once, with the parameter holding only the last object that arrived. The symptom is a function that quietly processes one item out of fifty.

ValueFromPipelineByPropertyName is the other form. It matches an incoming object's property to your parameter by name, which is how Get-Service | Stop-Service works: the service objects have a Name property and Stop-Service has a -Name parameter.

Inspect this example’s structure

Read the command and its supplied arguments. This example requires real PowerShell or its module; this diagram does not execute it.

function Get-NameLength { [CmdletBinding()] param( [Parameter(ValueFromPipeline)] [string] $Name ) process { [pscustomobject]@{ Name = $Name Length = $Name.Length } } } 'Spooler' , 'BITS' , 'WinRM' | Get-NameLength
  1. Stage 1functionPositional values or no named parameters
  2. Stage 2Get-NameLengthPositional values or no named parameters

    An example to inspect

    Preview the example. Sign in for the full lesson and activities.

    function Get-NameLength {
      [CmdletBinding()]
      param(
      [Parameter(ValueFromPipeline)]
    [string] $Name
    )
      process {
    [pscustomobject]@{
          Name = $Name
          Length = $Name.Length
        }
      } }
    'Spooler' , 'BITS' , 'WinRM' | Get-NameLength

    What happens

    Three strings go into the pipeline. Because -Name is marked ValueFromPipeline and the work is inside process, the block runs three times and emits three objects. Remove the process block and you get one result for WinRM only, because the parameter ends up holding just the last value.

    Worth remembering

    • ValueFromPipeline binds the whole incoming object to the parameter.
    • ValueFromPipelineByPropertyName matches by property name instead.
    • process runs once per object. begin and end run once each.
    • Pipeline work outside process silently handles only the last item.

    Primary documentation

    PowerShell objects and pipelines documentation

    Try it

    Illustrative output · values vary by environment

    1PS> function Get-NameLength {
    2PS> [CmdletBinding()]
    3PS> param(
    4PS> [Parameter(ValueFromPipeline)]
    5PS> [string] $Name
    6PS> )
    7PS> process {
    8PS> [pscustomobject]@{
    9PS> Name = $Name
    10PS> Length = $Name.Length
    11PS> }
    12PS> } }
    13PS> 'Spooler' , 'BITS' , 'WinRM' | Get-NameLength

    Example reader

    Inspect this page's example

    Fictional browser data when supported

    Run supported commands against the practice data, or inspect the example without executing it.

    Ready to inspect.

      A function that accepts pipeline input and runs once per object.

      Show reference output

      Illustrative output · values vary by environment

      Name     Length
      ----     ------
      Spooler  7
      BITS     4
      WinRM    5

      Keep going

      Next: PowerShell try, catch and finally

      Check your understanding

      An input object has a ComputerName property. When can a function bind that property automatically?

      Genie guidePowerShell Pipeline binding

      To make your function accept pipeline input, mark a parameter [Parameter(ValueFromPipeline)] and put your work in a process block.