Pffffieeuw… This was quite the puzzle! Finally got an easy progress bar when I need to execute those long-running batch commands.

Just use this function in your pipeline. It works the same as a Foreach-Object loop. Only it displays a progress bar 🙂

Example:

1..200 | Show-Progress { $_ }
Executing your command

How does it work?

Show-Progress is just a fancy way of using Write-Progress. It accepts 2 parameters: $Input and $ScriptBlock.

$Input

This parameter accepts your current object in the pipeline. It then adds a new property ItemNumber. $ItemNumber is used to calculate how much items are piped through. $PipelineBuffer then stores the entire object so that we can use this “new” object in our next pipe.

$ScriptBlock

Next, the scriptblock takes over. The $ScriptBlock parameter tries to be what it says it would be. That is, a scriptblock.

Since we essentially copied over the piped object to our $PipelineBuffer, we can easily use this object as if it were the original. Only addition is the $Itemnumber. So, we simply use this object to perform our scriptblock on.

Write-Progress

Now, we can calculate our $PercentageComplete. Since we used $a as a temporary counter and since we didn’t update it since our last object, this reflects the total amount of items in our original object.

$a = Total amount of items in original object
$ItemNumber = Number of the current item in the pipeline

$PercentageComplete = 100 / $a * ($_.ItemNumber + 1)

And here is the code:

function Show-Progress {
 
    param (
        [Parameter(
            Mandatory=$True,
            ValueFromPipeline=$True
            )]
        $Input,
 
        [parameter(
            Position=0,
            Mandatory=$True
            )]
        $ScriptBlock
    )
 
    $PipelineBuffer = $Input | ForEach-Object -Begin {$a = 0} {
        Add-Member -NotePropertyName ItemNumber -NotePropertyValue $a -InputObject $_ -PassThru
        $a ++
    }
 
    $PipelineBuffer | ForEach-Object {
        $PercentageComplete = 100 / $a * ($_.ItemNumber + 1)
        Write-Progress -PercentComplete $PercentageComplete -Activity "Executing command" -Status "$PercentageComplete% complete"
        Invoke-Command $ScriptBlock
    }
}

1 Comment

Sean · 06/05/2019 at 03:13

I actually ran into this problem not so long ago. I solved it a little differently. I declared a function that accepted an array of objects in the begin block. That function did the actual work of gathering the data for my report.

In the process block I did only some checking of the validity of each object and storing all the objects into an array. In the end block I send the array to the function. In this manner the function received an array so the normal methods of tracking progress worked.

Leave a Reply

Your email address will not be published. Required fields are marked *