1

I want to write a cmdlet that changes its behavior depending on its pipeline position. Simplyfied example:

Get-XYZObject | Rename-XYZObject

If the cmdlet is the last in the pipeline it should work directly on a database object.

Get-XYZObject | Rename-XYZObject | Set-XYZValue | Write-XYZObject

If it is not the endpoint it should pipe the object.

Is there a solution without providing a parameter to switch the behaviour?

I searched the web for a solution and read some tutorials about pipeline but found nothing. That could have two reasons:

  1. I do not know what to search for.
  2. It is just not possible.

I tend to the second cause the case

$renamedObject = Get-XYZObject | Rename-XYZObject

could be a problem. Would be nice if someone can clarify that.

Tobias Wollgam
  • 761
  • 2
  • 8
  • 25
  • 2
    No, there isn't really a runtime mechanism for indicating that. Usually you'd simply declare an extra switch parameter, like `-PassThru`, to make the command change its output behavior – Mathias R. Jessen Mar 23 '23 at 12:20
  • OK, that a parameter can solve that is obvious. But I really like your suggestion with `-PassThru`. Thanks! – Tobias Wollgam Mar 23 '23 at 12:26
  • @Darin I do not understand how this could work. Would be nice if you write an answer containing an example which clarifys your suggestion. – Tobias Wollgam Mar 23 '23 at 12:39
  • @TobiasWollgam, removed my answer. Sorry, I was thinking item position in the pipeline input, where you were wanting Cmdlet position in the pipeline chain. – Darin Mar 23 '23 at 18:14

1 Answers1

1

I think you might be looking for something similar to this, not sure how robust it is or if I would recommend you to use but might be of help.

You can use InvocationInfo.PipelineLength Property, InvocationInfo.PipelinePosition Property and InvocationInfo.Line Property to help you determine if your function is the last statement in a pipeline or even if your function is being piped.

function Test-PipePosition {
    [CmdletBinding()]
    param([Parameter(ValueFromPipeline)] $InputObject)

    begin {
        $lastInPipeline = $false

        # this would mean that the function is not being piped
        if($MyInvocation.Line.StartsWith($MyInvocation.InvocationName)) {
            return
        }

        if($MyInvocation.PipelineLength -eq $MyInvocation.PipelinePosition) {
            $lastInPipeline = $true
        }
    }
    process {
        # if it's not the last, then do stuff
        if(-not $lastInPipeline) {
            $InputObject
        }

    }
    end {
        # if it is the last then do other stuff
        if($lastInPipeline) {
            "I'm last in this pipeline!"
        }
    }
}

Here are some examples:

# Outputs `Get-Process`
Get-Process | Test-PipePosition | ForEach-Object { $_ }

# Outputs `I'm last in this pipeline!`
Get-Process | Test-PipePosition

# Doesn't produce output
Test-PipePosition

# Outputs `I'm last in this pipeline!`
0..10 | Test-PipePosition

Certainly this requires more testing and a much more robust solution might be possible by inspecting the AST.

Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37