Practical Examples of Write-Progress in PowerShell
Let’s take a look at some examples of how to use Write-Progress in PowerShell:
Example 1: Copy Files with Progress Bar
In this example, we are using Write-Progress to display the progress of a script that is copying files from one location to another.
$Files = Get-ChildItem -Path "C:\Source" $Destination = "C:\Destination" $TotalFiles = $Files.Count $Count = 0 Write-Progress -Activity "Copying Files" -Status "Starting" -PercentComplete 0 foreach ($File in $Files) { $Count++ $PercentComplete = (($Count / $TotalFiles) * 100) $Status = "Copying $($File.Name)" Write-Progress -Activity "Copying Files" -Status $Status -PercentComplete $PercentComplete Copy-Item $File.FullName $Destination -Force } Write-Progress -Activity "Copying Files" -Status "Complete" -PercentComplete 100
Example 2: Download Files with Progress Bar
In this example, we are using Write-Progress to display the progress of a script that downloads files from the internet.
$Urls = @( "https://www.microsoft.com/downloads/SharePointSP1.msi", "https://www.microsoft.com/downloads/SharePointSP2.msi", "https://www.microsoft.com/downloads/SharePointSP3.msi" ) # Destination folder $destinationFolder = "C:\Downloads" # Ensure the destination folder exists if (-not (Test-Path $destinationFolder)) { New-Item -Path $destinationFolder -ItemType Directory } # Download each file $totalUrls = $urls.Count for ($i=0; $i -lt $totalUrls; $i++) { $url = $urls[$i] $fileName = [System.IO.Path]::GetFileName($url) # Extract file name from URL $destinationPath = Join-Path -Path $destinationFolder -ChildPath $fileName # Display main progress Write-Progress -Activity "Downloading files" -Status ("Downloading " + $fileName) -PercentComplete (($i / $totalUrls) * 100) # Download file with sub-progress bar for individual file download Invoke-WebRequest -Uri $url -OutFile $destinationPath -Verbose } Write-Progress -Activity "Downloading files" -Completed -Status "All files downloaded!" Write-Host "All files downloaded successfully!" -ForegroundColor Green
Understanding the Write-Progress cmdlet in PowerShell
Write-Progress is a cmdlet in PowerShell that allows you to display a progress bar in the console. It has several parameters that allow you to customize the appearance of the progress bar, including the percentage complete, the status message, and the activity name. Here are the parameters that you can use with Write-Progress:
Basic Write-Progress syntax looks like:
Write-Progress [-Activity] <String> [[-Status] <String>] [-PercentComplete <Int32>] [-SecondsRemaining <Int32>] [-Id <Int32>] [-ParentId <Int32>] [-Completed] [-SourceId <Int32>] [-CurrentOperation <String>] [<CommonParameters>]
Here are the important parameters of the Write-Progress cmdlet:
Parameter | Description |
---|---|
Activity | The Activity parameter Specifies the title of the progress bar. This will appear as the first line of text. |
Status | This helps to display the current state of the activity or status of a running command in the status bar of the progress bar |
PercentComplete | 0 to 100 percent done |
SecondsRemaining | Countdown timer |
Id | Unique ID to update specific bar |
ParentId | Group child progress bars |
Write-Progres Example
- Use the
Write-Progress
cmdlet to create a new progress bar. - Specify the parameters, such as
-Activity
,-Status
,-PercentComplete
,-SecondsRemaining
, and-CurrentOperation
to customize the progress bar. - Update the progress bar periodically using the
Write-Progress
cmdlet with updated values for-PercentComplete
. - When the task is complete, use the
Complete
parameter to finish the progress bar.
To continually update the progress bar, we must place Write-Progress calls at certain points within your script’s logic, incrementing the PercentComplete each time. Here’s an example of a PowerShell script that uses the Write-Progress cmdlet to display a progress bar:
$Collection = 1..100 ForEach ($Item in $Collection) { Write-Progress -PercentComplete ($Item/100*100) -Status "Processing Items" -Activity "Item $item of 100" # Your actual script logic here Start-Sleep -Milliseconds 50 }
And here is what it looks like in PowerShell ISE:
Using Write-Progress in PowerShell is relatively straightforward. Here’s a step-by-step guide on how to use it:
Step 1: Define the variables
The first step is to define the variable or collection that you will be using in your script. These variables will have the collection of data set you’ll be processing.
Step 2: Start the progress bar and Perform Operation
The next step is to start the progress bar using the Write-Progress cmdlet and perform your target operation. You will need to specify the activity name and the status message.
Step 3: Update the progress bar
A common way to use Write-Progress is within a For loop in your script.
For($i=1; $i -le 100; $i++){ Write-Progress -Activity "Processing Files" -Status "$i% Complete" -PercentComplete $i # Perform loop action sleep -Milliseconds 100 }
This will iterate from 1 to 100, updating the progress bar each time with the current percent complete. You can update the status message to provide more context on which loop iteration is running.
Step 4: Complete the progress bar
Once your script has finished running, you have to complete the progress bar using the Write-Progress cmdlet. This will ensure that the progress bar is removed from the console.
Best practices for using progress bars in PowerShell
- Only use a progress bar when necessary: While progress bars can be helpful, they can also slow down your script if overused. Only use a progress bar when necessary. If a task finishes instantly or in a very short time frame, showing a progress bar might be unnecessary and even counterproductive, as it could flash on and off too quickly for the user to see.
- Update the progress bar frequently: Update the progress bar frequently to provide real-time feedback to the user. This will ensure that the user knows that the script is running and making progress.
- Use clear and concise status messages: Use the -Activity and -Status parameters to provide clear and concise information about what’s happening. Use the -CurrentOperation parameter to offer additional context, especially if it helps users understand the current task.
- Calculate Percentages Correctly: Ensure that your -PercentComplete value is accurately calculated. It should range from 0 to 100, representing the completion percentage.
- Use Nested Progress Bars for Multi-Level Tasks: If your script has tasks within tasks (like copying multiple folders where each folder has multiple files), use parent-child progress bars. Use the -Id and -ParentId parameters to manage and display nested progress bars correctly.
- Complete the Progress Bar: Use the -Completed switch to signal the end of the task, ensuring that the progress bar disappears or indicates completion. Also, Show progress at 0% before the operation starts.
- Test your script with large data sets: Test your script with large data sets to ensure that the progress bar is working properly and that it doesn’t slow down your script.
Troubleshooting common issues with progress bars in PowerShell
While using a progress bar in PowerShell is relatively straightforward, there are a few common issues that you may encounter. Here are a few tips for troubleshooting these issues:
1. The progress bar not updating
If the progress bar is not updating, ensure you update it frequently enough. Check your -PercentComplete calculation. Ensure that it’s updating as expected within the loop or process. For loops that process items quickly, consider adding a small delay with Start-Sleep for visual clarity.
If you are updating it frequently, and it still isn’t working, try running your script in a new PowerShell window.
2. Progress bar not displaying
If the progress bar is not displaying, ensure you start it with the Write-Progress cmdlet. If you are starting it with the Write-Progress cmdlet and it still isn’t working, ensure your loop or process is not finishing too quickly. The progress bar may not appear for very fast tasks.
3. Progress bar slowing down your script
4. Error: “Write-Progress : Cannot validate argument on parameter ‘PercentComplete’.
“The 110 is greater than the maximum allowed range of 100.”
- Cause: The value for ‘PercentComplete’ parameter exceeds the maximum limit of 100.
- Solution: Ensure the ‘PercentComplete’ parameter is within the range of 0 to 100.
Here is the Official Microsoft Documentation reference for Write-Progress cmdlet!
Progress Bar GUI in PowerShell
For GUI-based progress bars, the Windows.Forms.ProgressBar .NET control can be used.
Here is a sample script:
Add-Type -AssemblyName System.Windows.Forms $progressForm = New-Object System.Windows.Forms.Form $progressForm.Width = 300 $progressForm.Height = 150 $progressForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedSingle $progressForm.Text = "Processing data..." $progressBar = New-Object System.Windows.Forms.ProgressBar $progressBar.Location = New-Object System.Drawing.Point(10, 50) $progressBar.Size = New-Object System.Drawing.Size(280, 20) $progressForm.Controls.Add($progressBar) $progressLabel = New-Object System.Windows.Forms.Label $progressLabel.Location = New-Object System.Drawing.Point(10, 20) $progressLabel.Size = New-Object System.Drawing.Size(280, 20) $progressLabel.Text = "0% Complete" $progressLabel.TextAlign = [System.Drawing.ContentAlignment]::MiddleCenter $progressForm.Controls.Add($progressLabel) $progressForm.Show() $data = 1..100 $index = 1 # Loop through the data and update the progress bar foreach ($item in $data) { # Perform some operation on $item # Update the progress bar $progressPercent = ($index / $data.Count) * 100 $progressBar.Value = $progressPercent $progressLabel.Text = "$progressPercent% Complete" Start-Sleep -Milliseconds 100 $index++ } $progressForm.Close()
This creates a configurable progress bar by setting properties like minimum, maximum, and step values. The control can then be updated by incrementing $ProgressBar.Value
in your script.
Using Progress Bar with Multiple Operations
For extensive scripts involving multiple operations, you can segment the progress bar:
Write-Progress -Activity "Main Task" -Status "Task 1 of 3" -PercentComplete 33 # Task 1 logic Start-Sleep -Seconds 1 Write-Progress -Activity "Main Task" -Status "Task 2 of 3" -PercentComplete 66 # Task 2 logic Start-Sleep -Seconds 1 Write-Progress -Activity "Main Task" -Status "Task 3 of 3" -PercentComplete 100 # Task 3 logic Start-Sleep -Seconds 1
Progress Bar with Countdown Timer
To show a countdown timer in the progress bar, use the SecondsRemaining
parameter:
Write-Progress -Activity "Installing" -Status "Time Remaining" -SecondsRemaining $timeLeft
As $timeLeft decreases, the progress bar will visually count down the estimated time left. Here is a real-time example:
$progressParams = @{ Activity = "Processing data" Status = "In progress" PercentComplete = 0 SecondsRemaining = 60 CurrentOperation = "Initializing" } Write-Progress @progressParams $data = 1..10000 $index = 1 # Start the countdown timer $timer = [Diagnostics.Stopwatch]::StartNew() # Loop through the data and update the progress bar foreach ($item in $data) { # Perform some operation on $item # Update the progress bar $progressParams.PercentComplete = ($index / $data.Count) * 100 $progressParams.CurrentOperation = "Processing item $index of $($data.Count)" $progressParams.SecondsRemaining = (($timer.Elapsed.TotalSeconds / $index) * ($data.Count - $index)).ToString("F0") Write-Progress @progressParams $index++ } # Complete the progress bar Write-Progress -Completed -Activity "Completed"
Here is what it looks like in PowerShell ISE:
Here are a few more examples of Count down timers using the PowerShell Write-Progress cmdlet:
Function Start-Countdown { param ( [Parameter(Mandatory=$true)] [int]$Seconds ) # Countdown start time $StartTime = Get-Date $ElapsedTime =0 # While there are still seconds left While ($ElapsedTime -lt $seconds) { # Calculate elapsed time $ElapsedTime = [math]::Round(((Get-Date) - $StartTime).TotalSeconds,0) # Update the progress bar Write-Progress -Activity "Counting down..." -Status ("Time left: " + ($Seconds - $ElapsedTime) + " seconds") -PercentComplete (($elapsedTime / $Seconds) * 100) #$Seconds = $Seconds - $elapsedTime # Wait for a second Start-Sleep -Seconds 1 } # Once countdown is complete, close the progress bar Write-Progress -Activity "Counting down..." -Completed } # Call the function to start a 10-second countdown Start-Countdown -Seconds 10
Here is another example of how to show the Progress Bar in While Loop:
# Set total time for countdown timer $timeRemaining = 20 while($timeRemaining -gt 0){ # Calculate percentage $percentComplete = (($timeRemaining / 60) * 100) # Update progress bar Write-Progress -Activity "Deployment in progress" -Status "Time remaining: $timeRemaining seconds" -PercentComplete $percentComplete -SecondsRemaining $timeRemaining # Decrement timer $timeRemaining-- # Wait 1 second Start-Sleep -Seconds 1 }
Nested (Parent-Child) Progress Bars for multi-level Operations
Parent-child progress bars are especially useful when you have a multi-level process. Here’s an example of a parent-child progress bar using Write-Progress. Let’s consider you’re processing multiple files, and for each file, you have several tasks to complete:
# Parent loop - processing files 1..5 | ForEach-Object { $fileNumber = $_ # Parent progress Write-Progress -Activity "Processing Files" -Status "File $fileNumber of 5" -PercentComplete (($fileNumber / 5) * 100) -id 0 # Simulate some file-specific tasks in a child loop 1..10 | ForEach-Object { $taskNumber = $_ # Child progress (nested within the parent) Write-Progress -Activity "Performing Task on File $fileNumber" -Status "Task $taskNumber of 10" -PercentComplete (($taskNumber / 10) * 100) -ParentId 0 # Simulating time taken for each task with sleep Start-Sleep -Milliseconds 100 } } # Completing the parent progress bar Write-Progress -Activity "Processing Files" -Completed -Status "All files processed!"
The -ParentId parameter in Write-Progress for the child progress bar tells PowerShell that this progress bar is a child of another progress bar. The ID 0 typically refers to the most recent parent Write-Progress bar. Output:
When you run the above script, you’ll see a top-level progress bar for file processing and a nested progress bar for tasks on each file.
Benefits of using a progress bar in PowerShell
1. Real-time Visual feedback
2. Time estimation to Track progress
A progress bar can also provide an estimate of how long your script will take to complete. This estimate can be incredibly helpful, especially when running scripts that take a long time to complete. It allows you to plan your time accordingly and ensures that you don’t waste time waiting for your script to finish.
3. Improved user experience
Using progress bars in PowerShell ForEach loops
Using a progress bar in a foreach loop can be incredibly helpful, especially when iterating over a large number of items. Here’s an example of how to use a progress bar in a foreach loop:
$Items = 1..10000 Write-Progress -Activity "Processing Items" -Status "Starting" -PercentComplete 0 foreach ($Item in $Items) { $PercentComplete = (($Item / $Items.Count) * 100) $Status = "Processing Item $($Item)" Write-Progress -Activity "Processing Items" -Status $Status -PercentComplete $PercentComplete # Do some processing } Write-Progress -Activity "Processing Items" -Status "Complete" -PercentComplete 100
Conclusion and final thoughts
What is a PowerShell progress bar?
Why would I use a Progress Bar in PowerShell?
How can I add a progress bar to my PowerShell script?
Is it possible to display multiple progress bars at once?
PowerShell supports nested progress bars, allowing you to display a parent progress bar and one or more child progress bars simultaneously.
How do I update the progress bar during a loop?
Inside a loop, you can update the progress bar by calling Write-Progress
with updated values for -PercentComplete
and other parameters at each iteration.
How do I remove the progress bar once the operation is complete?
The progress bar will automatically disappear once the script is completed. You can also manually remove it by calling Write-Progress
with the -Completed
parameter. For example: Write-Progress -Activity "Processing" -Status "Completed" -Completed