Pipeline-Aware Functions
Prerequisites: Advanced functions
To accept pipeline input like a real cmdlet, a function needs
ValueFromPipeline and a process block.
function ConvertTo-Celsius {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[double]$Fahrenheit
)
begin { Write-Verbose 'Starting conversion' }
process { [math]::Round(($Fahrenheit - 32) * 5 / 9, 1) }
end { Write-Verbose 'Done' }
}
32, 68, 212 | ConvertTo-Celsius
ConvertTo-Celsius -Fahrenheit 98.6
0
20
100
37
beginruns once, before any input — setup.processruns once per pipeline object, with the parameter set to that object.endruns once, after all input — cleanup or final output.
Without a process block, the body runs as end, so the function only
sees the last piped item — a classic bug.
Binding by property name
ValueFromPipelineByPropertyName binds a parameter to the property with
the same name (or alias) on each piped object:
function Test-Port {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipelineByPropertyName)]
[Alias('Host')]
[string]$ComputerName,
[Parameter(ValueFromPipelineByPropertyName)]
[int]$Port = 443
)
process {
[pscustomobject]@{ ComputerName = $ComputerName; Port = $Port; Checked = $true }
}
}
$targets = @(
[pscustomobject]@{ Host = 'web01'; Port = 80 }
[pscustomobject]@{ Host = 'db01'; Port = 5432 }
[pscustomobject]@{ Host = 'api01' }
)
$targets | Test-Port
ComputerName Port Checked
------------ ---- -------
web01 80 True
db01 5432 True
api01 443 True
This is how Import-Csv servers.csv | Test-Port just works: CSV columns
become properties that bind to parameters.
Accepting both arrays and pipeline input
function Get-Initial {
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[string[]]$Name
)
process {
foreach ($n in $Name) { $n.Substring(0, 1).ToUpper() }
}
}
(Get-Initial -Name ada, linus) -join ''
('grace', 'alan' | Get-Initial) -join ''
AL
GA
With pipeline input, $Name holds one item per process call; passed
directly, it holds the whole array. The inner foreach handles both.
Streaming
Output each result from process as soon as it's ready — don't collect
everything into a list and output it in end. Streaming keeps memory low
and lets Select-Object -First 5 stop the pipeline early.