代理命令,分流命令

huangapple go评论71阅读模式
英文:

Proxy command that tees

问题

对于一个 PowerShell 功能请求(无论这是否是个好主意),我尝试创建了一个原型,使用了一个名为 Write-Table 的代理命令,基于 Format-Table 命令,它直接将数据写入主机,并根据 PassThru 开关的情况应该传递当前输入对象:

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
    
    #>
}

但不知何故,似乎当前的对象在传递给主机之前已从管道中移除(我不知道如何防止这种情况发生):

gci *.txt | Write-Table -PassThru | Get-Item # 最终意图: ... | 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.

是否有人能解释这里发生了什么问题以及如何解决

<details>
<summary>英文:</summary>

For a [PowerShell feature request](https://github.com/PowerShell/PowerShell/issues/20001) (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=&#39;https://go.microsoft.com/fwlink/?LinkID=2096703&#39;)]
        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(&#39;CoreOnly&#39;,&#39;EnumOnly&#39;,&#39;Both&#39;)]
            [string]
            ${Expand},
        
            [Parameter(ValueFromPipeline=$true)]
            [psobject]
            ${InputObject},
        
            [switch]
            ${PassThru})
        
        begin
        {
            try {
                $outBuffer = $null
                if ($PSBoundParameters.TryGetValue(&#39;OutBuffer&#39;, [ref]$outBuffer))
                {
                    $PSBoundParameters[&#39;OutBuffer&#39;] = 1
                }
        
                if ($PSBoundParameters.TryGetValue(&#39;PassThru&#39;, [ref]$outBuffer))
                {
                    $Null = $PSBoundParameters.Remove(&#39;PassThru&#39;)
                }
        
                $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand(&#39;Microsoft.PowerShell.Utility\Format-Table&#39;, [System.Management.Automation.CommandTypes]::Cmdlet)
                $scriptCmd = {&amp; $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()
            }
        }
        &lt;#
        
        .ForwardHelpTargetName Microsoft.PowerShell.Utility\Format-Table
        .ForwardHelpCategory Cmdlet
        
        #&gt;
    }

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 &#39;Microsoft.PowerShell.Commands.Internal.Format.FormatStartData&#39; because it does not exist.
    Get-Item: Cannot find path &#39;Microsoft.PowerShell.Commands.Internal.Format.GroupStartData&#39; because it does not exist.
    Get-Item: Cannot find path &#39;Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData&#39; because it does not exist.
    -a---           7/20/2023  3:44 PM              0 file2.txt
    Get-Item: Cannot find path &#39;Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData&#39; because it does not exist.
    -a---           7/20/2023  3:44 PM              0 file3.txt
    Get-Item: Cannot find path &#39;Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData&#39; because it does not exist.
    Get-Item: Cannot find path &#39;Microsoft.PowerShell.Commands.Internal.Format.GroupEndData&#39; because it does not exist.
    Get-Item: Cannot find path &#39;Microsoft.PowerShell.Commands.Internal.Format.FormatEndData&#39; because it does not exist.

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


</details>


# 答案1
**得分**: 2

以下是您要翻译的部分

看起来对包装的命令稍作更新应该能解决您的问题如果我理解正确`Format-Table` 生成的输出应该直接发送到主机因此需要添加 `Out-Host`然后如果存在 `-PassThru`您很可能希望将原始对象发送到管道中因此

```sh
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()
        }
    }
    &lt;#

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

    #&gt;
}

然后,类似这样的代码:

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

可以正常工作:

PS ..\pwsh&gt; $capture = 0..10 | ForEach-Object { [pscustom....  # 输出发送到主机: Item ----    0    1    2    3    4    5
PS ..\pwsh&gt; $capture
0
1
2
3
4
5
6
7
8
9
10
英文:

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 = &#39;https://go.microsoft.com/fwlink/?LinkID=2096703&#39;)]
    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(&#39;CoreOnly&#39;, &#39;EnumOnly&#39;, &#39;Both&#39;)]
        [string]
        ${Expand},

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

        [switch]
        ${PassThru})

    begin {
        $null = $PSBoundParameters.Remove(&#39;PassThru&#39;)
        $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()
        }
    }
    &lt;#

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

    #&gt;
}

Then something like this:

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

Works just fine:

PS ..\pwsh&gt; $capture = 0..10 | ForEach-Object { [pscustom....
# Output sent to the host:
Item
----
0
1
2
3
4
5
PS ..\pwsh&gt; $capture
0
1
2
3
4
5
6
7
8
9
10

huangapple
  • 本文由 发表于 2023年7月20日 22:32:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/76730959.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定