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.