Some cmdlets have a progress bar built-in. Here’s how to make your own Powershell progress bar with the Write-Progress cmdlet.
What’s happening?
In my last post, I covered how to hide PowerShell progress bars you encounter with some built-in cmdlets. More often than not though, you probably want to keep these progress bars as they are typically there to give the script executor feedback that the script is indeed running, and how far along it is in step execution.
Microsoft has done a good job recognizing which cmdlets likely will have longer execution times (like Install-WindowsFeature), but what if you have a process that you know could take a while to complete? How can you give your user feedback that the script is actively working and to give an idea of how much work has been done, while also giving an impression of how much work is left to do?
Using Write-Progress for the DIY progress bar
PowerShell has included Write-Progress since version 1.0 for filling the need of scripted progress bars. Here are the available parameters:
Write-Progress [-Activity] <String> [[-Status] <String>] [[-Id] <Int32>] [-PercentComplete <Int32>] [-SecondsRemaining <Int32>] [-CurrentOperation <String>] [-ParentId <Int32>] [-Completed] [-SourceId <Int32>] [<CommonParameters>]
Let’s take a look at a frame and point out the how the different parameters factor in. Here is the sample script we’ll run to generate the progress bar. The sample output is shown for both the console and the PowerShell ISE, as they do have slightly different display formats. NOTE: if you are using VS Code, you won’t see a progress bar at all (known issue).
For ($i=0; $i -le 100; $i++) { Start-Sleep -Milliseconds 20 Write-Progress -Activity "ActivityString" -Status "StatusString" -PercentComplete $i -CurrentOperation "CurrentOperationString" }
- Activity: This string at the top of the progress bar (ActivityString). It is intended to describe the overarching process for which we are tracking progress.
- Status: This string is displayed underneath Activity. It is intended to describe the current “status” or state of the overarching process.
- CurrentOperation: The display of this string differs between the console and ISE. This string is intended to describe the current process step being executed .
Modifying our example above but taking intent into account, this would be one way to populate these parameters:
For ($i=0; $i -le 100; $i++) { Start-Sleep -Milliseconds 20 Write-Progress -Activity "Counting to 100" -Status "Current Count: $i" -PercentComplete $i -CurrentOperation "Counting ..." }
Nesting, replacing or superimposing progress bars
If you run Install-WindowsFeature, you will notice that it appears to only use the Status parameter, but that the value changes during execution as the progress bar reaches completion. Though this looks like a single progress bar, what we are actually watching is a new progress bar replace the previous progress bar. If we are running two progress bars simultaneously, we can control whether or not to nest them or superimpose them.
There is another parameter, Id, which determines if a new progress bar is shown under an already running progress bar, or if it is superimposed over the top. If Id is not set or is the same for two different Write-Progress executions, they will be superimposed. However if Id is different between the executions, the second progress will appear under the first progress bar. You commonly see this at play with nested operations.
Nested progress bars
Here is an example of nested progress bars with different ids, which will show us two progress bars:
For ($i=0; $i -le 100; $i++) { Start-Sleep -Milliseconds 1 Write-Progress -Id 1 -Activity "First Write Progress" -Status "Current Count: $i" -PercentComplete $i -CurrentOperation "Counting ..." For ($j=0; $j -le 100; $j++) { Start-Sleep -Milliseconds 1 Write-Progress -Id 2 -Activity "Second Write Progress" -Status "Current Count: $j" -PercentComplete $j -CurrentOperation "Counting ..." } }
Replacing the previous progress bar
Much like Install-WindowsFeature, we can make it look like the same progress bar running but changing Activity, Status, or CurrentOperation text, though what we are really doing is replacing a progress bar as it completes with a new one.
For ($i=0; $i -le 100; $i++) { Start-Sleep -Milliseconds 20 If ($i -le 50) { Write-Progress -Id 1 -Activity "First Write Progress" -Status "Current Count: $i" -PercentComplete $i -CurrentOperation "Counting ..." } Else { Write-Progress -Id 1 -Activity "Second Write Progress" -Status "Current Count: $i" -PercentComplete $i -CurrentOperation "Counting ..." } }
Progress bars – a sequence of still frames
A key takeaway here is to understand that every time we run Write-Progress, we are in essence creating a a single frame in a reel of frames, which give us the impression of animation. In the previous example, the changing values within a status bar is an animation illusion; we have started painting a completely different set of frames.
Reference
- Write-Progress | docs.microsoft.com
João Santana says
Hi.
I want to show a progress bar while Update-Module is running. How can I do it? Thanks.
Aaron Rothstein says
I don’t think you can display a progress bar that is directly tied to the progress of the updating module; that would have to be incorporated into the Update-Module cmdlet directly.