Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Proxy command that tees

For a PowerShell feature request (whether this is a good idea or not), I tried to create a prototype using a proxy command named Write-Table based on the Format-Table cmdlet that directly writes to the host and depending on the PassThru switch should pass the current input object:

function Write-Table {
    [CmdletBinding(HelpUri='https://go.microsoft.com/fwlink/?LinkID=2096703')]
    param(
        [switch]
        ${AutoSize},
    
        [switch]
        ${RepeatHeader},
    
        [switch]
        ${HideTableHeaders},
    
        [switch]
        ${Wrap},
    
        [Parameter(Position=0)]
        [System.Object[]]
        ${Property},
    
        [System.Object]
        ${GroupBy},
    
        [string]
        ${View},
    
        [switch]
        ${ShowError},
    
        [switch]
        ${DisplayError},
    
        [switch]
        ${Force},
    
        [ValidateSet('CoreOnly','EnumOnly','Both')]
        [string]
        ${Expand},
    
        [Parameter(ValueFromPipeline=$true)]
        [psobject]
        ${InputObject},
    
        [switch]
        ${PassThru})
    
    begin
    {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
            {
                $PSBoundParameters['OutBuffer'] = 1
            }
    
            if ($PSBoundParameters.TryGetValue('PassThru', [ref]$outBuffer))
            {
                $Null = $PSBoundParameters.Remove('PassThru')
            }
    
            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Format-Table', [System.Management.Automation.CommandTypes]::Cmdlet)
            $scriptCmd = {& $wrappedCmd @PSBoundParameters }
    
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }
    
    process
    {
        try {
            if ($PassThru) { $_ }
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }
    
    end
    {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
    
    clean
    {
        if ($null -ne $steppablePipeline) {
            $steppablePipeline.Clean()
        }
    }
    <#
    
    .ForwardHelpTargetName Microsoft.PowerShell.Utility\Format-Table
    .ForwardHelpCategory Cmdlet
    
    #>
}

But somehow it appears that the current object is removed from the pipeline before it is also passed to the host (I have no clue how to prevent that)

gci *.txt | Write-Table -PassThru | Get-Item # Final intention: ... | Remove-Item

    Directory: C:\Users\Gebruiker\Downloads\temp

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---           7/20/2023  3:44 PM              0 file1.txt
Get-Item: Cannot find path 'Microsoft.PowerShell.Commands.Internal.Format.FormatStartData' because it does not exist.
Get-Item: Cannot find path 'Microsoft.PowerShell.Commands.Internal.Format.GroupStartData' because it does not exist.
Get-Item: Cannot find path 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData' because it does not exist.
-a---           7/20/2023  3:44 PM              0 file2.txt
Get-Item: Cannot find path 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData' because it does not exist.
-a---           7/20/2023  3:44 PM              0 file3.txt
Get-Item: Cannot find path 'Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData' because it does not exist.
Get-Item: Cannot find path 'Microsoft.PowerShell.Commands.Internal.Format.GroupEndData' because it does not exist.
Get-Item: Cannot find path 'Microsoft.PowerShell.Commands.Internal.Format.FormatEndData' because it does not exist.

Does anyone have an explanation where this goes sideways and how I might resolve this?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

It seems a little update to the wrapped command should solve your problem if I’m understanding correctly, the output generated by Format-Table should go directly to the host, thus adding Out-Host. Then if -PassThru is present you more than likely want to send the original object thru the pipeline so:

function Write-Table {
    [CmdletBinding(HelpUri = 'https://go.microsoft.com/fwlink/?LinkID=2096703')]
    param(
        [switch]
        ${AutoSize},

        [switch]
        ${RepeatHeader},

        [switch]
        ${HideTableHeaders},

        [switch]
        ${Wrap},

        [Parameter(Position = 0)]
        [System.Object[]]
        ${Property},

        [System.Object]
        ${GroupBy},

        [string]
        ${View},

        [switch]
        ${ShowError},

        [switch]
        ${DisplayError},

        [switch]
        ${Force},

        [ValidateSet('CoreOnly', 'EnumOnly', 'Both')]
        [string]
        ${Expand},

        [Parameter(ValueFromPipeline = $true)]
        [psobject]
        ${InputObject},

        [switch]
        ${PassThru})

    begin {
        $null = $PSBoundParameters.Remove('PassThru')
        $scriptCmd = { Microsoft.PowerShell.Utility\Format-Table @PSBoundParameters | Out-Host }
        $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
        $steppablePipeline.Begin($PSCmdlet)
    }

    process {
        if ($PassThru.IsPresent) {
            $InputObject
        }

        $steppablePipeline.Process($InputObject)
    }

    end {
        $steppablePipeline.End()
    }

    clean {
        if ($null -ne $steppablePipeline) {
            $steppablePipeline.Clean()
        }
    }
    <#

    .ForwardHelpTargetName Microsoft.PowerShell.Utility\Format-Table
    .ForwardHelpCategory Cmdlet

    #>
}

Then something like this:

$capture = 0..5 | ForEach-Object { [pscustomobject]@{ Item = $_ } } |
    Write-Table -PassThru |
    ForEach-Object Item

Works just fine:

PS ..\pwsh> $capture = 0..10 | ForEach-Object { [pscustom....

# Output sent to the host:
Item
----
   0
   1
   2
   3
   4
   5

PS ..\pwsh> $capture
   
0
1
2
3
4
5
6
7
8
9
10
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading