如何在管道功能中使用Write-Progress?

oɔɯ*_*ɯǝɹ 5 powershell

我正在尝试编写一个PowerShell接受管道输入的函数.我想显示一个进度条,使用Write-Progress该管道中每个项目的增量.

例如:

function Write-PipelineProgress {
    [Cmdletbinding()]
    Param
    (
        [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] `
        [object[]] $Input,

        [string] $Activity = "Processing items"       
    )

    Begin { Write-Progress -Activity $Activity -Status "Preparing" }

    Process {
        # how do i determine how much progress we've made?
        $percentComplete = ... ?
        Write-Progress -Activity $Activity -Status "Working" -PercentComplete $percentComplete

        # return current item, so processing can continue
        $_
    }

    End { Write-Progress -Activity $Activity -Status "End" -Completed }
}

Get-ChildItem | Write-PipelineProgress -Activity "Listing files"
Run Code Online (Sandbox Code Playgroud)

我如何确定进度(完成百分比)?

Gra*_*old 6

您需要知道管道中的项目数以跟踪进度.

Powershell 3.0可以让您计算管道中的内容.Count,$Input如果正确声明管道参数,则无需执行任何工作,除了访问属性.它还意味着您可以取消begin {} process {} end {}块,只需要一个简单的功能.

早期版本没有该Count属性,因此您首先必须迭代并捕获管道以获取计数然后再次处理,这不是那么有效,我将在稍后展示.

Powershell V2版本:

function Show-ProgressV2{
    param (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [PSObject[]]$InputObject,
        [string]$Activity = "Processing items"
    )

    Begin {$PipeArray = @()}

    Process {$PipeArray+=$InputObject}

    End {
        [int]$TotItems = ($PipeArray).Count
        [int]$Count = 0

        $PipeArray|foreach {
            $_
            $Count++
            [int]$percentComplete = [int](($Count/$TotItems* 100))
            Write-Progress -Activity "$Activity" -PercentComplete "$percentComplete" -Status ("Working - " + $percentComplete + "%")
            }
        }
}    
Run Code Online (Sandbox Code Playgroud)

Powershell V3版本:

function Show-ProgressV3{
    [CmdletBinding()]
    param (
        [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
        [PSObject[]]$InputObject,
        [string]$Activity = "Processing items"
    )

        [int]$TotItems = $Input.Count
        [int]$Count = 0

        $Input|foreach {
            $_
            $Count++
            [int]$percentComplete = ($Count/$TotItems* 100)
            Write-Progress -Activity $Activity -PercentComplete $percentComplete -Status ("Working - " + $percentComplete + "%")
        }
}
Run Code Online (Sandbox Code Playgroud)

效率
比较两者,V3功能大约快5-6倍,具体取决于管道的大小.

考虑以下预过滤文件列表,查找.jpg我家驱动器中的所有文件,选择前200个文件并排序和列出,根据您的评论在管道中使用该功能3次:

$V2 = Measure-Command {Get-ChildItem -Filter *.jpg -Recurse `
| Show-ProgressV2 -Activity "Selecting" `
| Select-Object -First 200 `
| Show-ProgressV2 -Activity "Sorting" `
| Sort-Object -Property FullName `
| Show-ProgressV2 -Activity "Listing" `
| FL}

$V3 = Measure-Command {Get-ChildItem -filter *.jpg -Recurse `
| Show-ProgressV3 -Activity "Selecting" `
| Select-Object -First 200 `
| Show-ProgressV3 -Activity "Sorting" `
| Sort-Object -Property FullName `
| Show-ProgressV3 -Activity "Listing" `
| FL}

$V2  
$V3
Run Code Online (Sandbox Code Playgroud)

这给了我以下时间:

PS C:\Users\Graham> C:\Users\Graham\Documents\Stack_ShowProgress_Pipeline.ps1


Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 48
Milliseconds      : 360
Ticks             : 483607111
TotalDays         : 0.000559730452546296
TotalHours        : 0.0134335308611111
TotalMinutes      : 0.806011851666667
TotalSeconds      : 48.3607111
TotalMilliseconds : 48360.7111

Days              : 0
Hours             : 0
Minutes           : 0
Seconds           : 8
Milliseconds      : 335
Ticks             : 83358374
TotalDays         : 9.6479599537037E-05
TotalHours        : 0.00231551038888889
TotalMinutes      : 0.138930623333333
TotalSeconds      : 8.3358374
TotalMilliseconds : 8335.8374
Run Code Online (Sandbox Code Playgroud)

  • 是的,该参数已定义,但在`Begin {...}`作用域中不可用。问题在于,管道输入是一种惰性评估。在`Process {...}`范围中,`$ Input`只有一个值,这是有意义的,因为对每个值都执行`Process {...}`。 (2认同)