PowerShell:有没有办法获得通过管道输送到函数中的对象的总数?

人气:632 发布:2022-10-16 标签: scripting powershell pipeline

问题描述

编辑:添加说明这一点我只想显示一个进度条,其中已知结束(例如,管道的结束),以便函数可以提供接近完成的百分比(通常是数百或数千的大集)。我偶尔会编写函数,从两个管道或通过一个参数获取对象,以便函数可以灵活。

从参数传入的对象数组的进度条非常简单,相当于首先拉入完整的管道集,然后再次处理它们。我一直在避免后者,在这种情况下我干脆放弃写入进度,因为它不值得受到影响。

我不记得在哪里看到的,但有人提到了$PSCmdlet.MyInocation可能提供了计数,但可能我解释错了。

我编写了一些接受管道输入的函数,并且通常希望为通过管道进入该函数的所有对象编写一个百分比进度条。

有没有办法获取函数开始时的总计数?

我知道当函数循环遍历管道对象时如何增加计数器,但这只能得到到目前为止处理的对象的数量。我想通过对整个管道计数进行计算来获得百分比。

我查看了$MyInocation和$PSCmdlet.MyInocation属性,但无论管道集有多大,管道长度和管道位置值区域都始终为‘2’。

注意:这不是我作为解决方案关注的重点,它只是我发现有希望的事情之一

这是一个测试:

Function Test-Pipeline {
[CmdletBinding()]

PARAM(
    [Parameter(ValueFromPipeLine=$true,ValueFromPipelineByPropertyName=$true)]
    [Alias("FullName")]
    [psobject[]]$Path
)

BEGIN{
    $PSCmdlet.MyInvocation | Select *
}
PROCESS{
    ForEach ($xpath in $Path) {
        $filepath = Resolve-Path $xpath | Select -ExpandProperty Path
    }
}
END{}
}

当我通过管道输入dir(此文件夹包含20个项目)的内容时,我得到:

PS C:Temp> Dir | Test-Pipeline

MyCommand             : Test-Pipeline
BoundParameters       : {}
UnboundArguments      : {}
ScriptLineNumber      : 1
OffsetInLine          : 7
HistoryId             : 213
ScriptName            : 
Line                  : Dir | Test-Pipeline
PositionMessage       : At line:1 char:7
                        + Dir | Test-Pipeline
                        +       ~~~~~~~~~~~~~
PSScriptRoot          : 
PSCommandPath         : 
InvocationName        : Test-Pipeline
PipelineLength        : 2
PipelinePosition      : 2
ExpectingInput        : True
CommandOrigin         : Runspace
DisplayScriptPosition : 

推荐答案

如前所述,如果您想知道您的cmdlet中将收到多少对象以预定义进度条的单位,您基本上无法(如果不中断流进程)。 任何看似做了您想做的事情的解决方案实际上都会阻塞整个管道,因为它会收集所有对象(在内存中),对它们进行计数,最后一次释放它们。 这将违反Strongly Encouraged Development GuidelinesTOWrite Single Records to the Pipeline并导致较高的内存使用率。

说明

想象一条装配线,你负责给所有经过你工位的物体上色。现在你想知道今天你需要做多少件事。除非您的站点(Cmdlet)之外的人告诉您有多少人会跟随,否则您只能通过接收所有对象对它们进行计数(仍为上色)和传递来确定。由于所有这些操作都需要时间(以及物品的储藏室),下一站的人不会高兴,因为他将一次收到所有物品,而且比预期要晚得多...

技术

让我们构建一个cmdlet,它从ProcessList

生成无色对象
function Create-Object {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeLine=$true)]$ProcessList,
        [Switch]$Show
    )
    begin {
        $Index = 0
    }
    process {
        $Object = [PSCustomObject]@{
            Index = $Index++
            Item  = $ProcessList
            Color = 'Colorless'
        }
        if ($Show) { Write-Host 'Created:' $Object.PSObject.Properties.Value }
        $Object
    }
}

这就是给对象上色的你:

function Color-Object {
    [CmdletBinding()]
    param(
        [Parameter(ValueFromPipeLine=$true)]$InputObject,
        [Switch]$Show
    )
    process {
        $InputObject.Color = [ConsoleColor](Get-Random 16)
        if ($Show) { Write-Host 'Colored:' $InputObject.PSObject.Properties.Value }
        $InputObject
    }
}

结果如下:

'a'..'e' |Create-Object |Color-Object

Index Item       Color
----- ----       -----
    0    a DarkMagenta
    1    b      Yellow
    2    c        Blue
    3    d        Gray
    4    e       Green

现在让我们看看事情是如何实际处理的:

'a'..'e' |Create-Object -Show |Color-Object -Show

Created: 0 a Colorless
Colored: 0 a DarkGreen

Created: 1 b Colorless
Colored: 1 b DarkRed
Created: 2 c Colorless
Colored: 2 c Gray
Created: 3 d Colorless
Colored: 3 d DarkGreen
Created: 4 e Colorless
Colored: 4 e DarkGray
Index Item     Color
----- ----     -----
    0    a DarkGreen
    1    b   DarkRed
    2    c      Gray
    3    d DarkGreen
    4    e  DarkGray

如您所见,a";(索引0)在第二项b(1)已创建! 换句话说,Create-Object还没有创建所有对象,也无法知道接下来会创建多少对象。除了像前面解释的那样只是等待它们之外,如果有很多对象,这些对象是胖的(PowerShell对象通常是胖的),或者是在输入速度很慢的情况下(另请参阅:Advocating native PowerShell)。这意味着,如果枚举(计数)对象对于进程的其余部分来说是微不足道的,则可能需要例外:例如,收集文件信息(Get-ChildItem)以在以后调用繁重的进程(例如Get-FileHash)。

327