英文:
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='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?
</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()
}
}
<#
.ForwardHelpTargetName Microsoft.PowerShell.Utility\Format-Table
.ForwardHelpCategory Cmdlet
#>
}
然后,类似这样的代码:
$capture = 0..5 | ForEach-Object { [pscustomobject]@{ Item = $_ } } |
Write-Table -PassThru |
ForEach-Object Item
可以正常工作:
PS ..\pwsh> $capture = 0..10 | ForEach-Object { [pscustom.... # 输出发送到主机: Item ---- 0 1 2 3 4 5
PS ..\pwsh> $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 = '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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论