Table of contents
This came up on IRC in the context of determining whether a function was the last function on the pipeline, in order to create formatted output in that case, and output objects otherwise. Before I give the solution, I just want to say: don't do that.
That is, don't change your output based on where you are in a pipeline.
There are numerous scenarios where your function will be the last one on a pipeline, but still be participating in further pipelines, including formatting and output modification. For example, take our function (defined below) Test-Pipeline in these three scenarios below. In none of these scenarios would it be appropriate for the function to write formatted output instead of outputting the raw object, but in each case, the function is the last function in the pipeline.
# Assignment
$order = Get-ChildItem | Test-Pipeline
$order | Format-Table *
# Nested Pipelines
Get-ChildItem | Where-Object { $_.PsIsContainer} | ForEach-Object {
Get-ChildItem $_ | Test-Pipeline
} | Select-Object Pipe*
# Nested Expressions
@( Get-ChildItem | Test-Pipeline )[0].PipelineLength | ForEach-Object { $_ }
However, if you want to determine your function's position in the pipeline for some other reason, the answer is simple. You need to use $MyInvocation and compare the PipelineLength and PipelinePosition properties:
## Useful for testing all sorts of things about the pipeline
function Test-Pipeline {
[CmdletBinding()]
Param(
[Parameter(ValueFromPipeline=$true)]
[PSObject]$InputObject,
[Switch]$Passthru,
[Switch]$PassCmdlet
)
BEGIN {
Write-Output $MyInvocation
if($PassCmdlet) {
Write-Output $PsCmdlet
}
}
PROCESS { if($Passthru){ $_ } }
}
## Shows
function Test-LastInPipeline {
Param(
[Parameter(ValueFromPipeline=$true)]
[PSObject]$InputObject,
[Switch]$Passthru
)
BEGIN {
$IsLast = $MyInvocation.PipelineLength -eq $MyInvocation.PipelinePosition
if(!$IsLast) { $MyInvocation }
}
PROCESS { if($Passthru){ $_ } }
}

Comments