In the simplest case, use the following:
function tail { if ($MyInvocation.ExpectingInput) { # Pipeline input present. # $Input passes the collected pipeline input through. $Input | coreutils tail @args } else { coreutils tail @args } }
The down-side of this approach is that all pipeline input is collected in memory first, before it is relayed to the target program.
A streaming solution - where input objects (lines) - are passed through as they become available - requires more effort:
function tail { [CmdletBinding(PositionalBinding=$false)] param( [Parameter(ValueFromPipeline)] $InputObject , [Parameter(ValueFromRemainingArguments)] [string[]] $PassThruArgs ) begin { # Set up a steppable pipeline. $scriptCmd = { coreutils tail $PassThruArgs } $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($PSCmdlet) } process { # Pass the current pipeline input through. $steppablePipeline.Process($_) } end { $steppablePipeline.End() } }
The above advanced function is a so-called proxy function, explained in more detail in this answer.