Progress Bar Series (Part 5) – Display Progress in a non-GUI Script

In Part 5 of the Progress Bar series we take a look at the Write-Progress cmdlet, which can be used to display task progress in a non-GUI script. This cmdlet is available in Windows PowerShell and PowerShell Core (cross-platform).

Windows PowerShell

PowerShell (Core) – Windows OS

PowerShell (Core) – Linux

There are two essential ways to use this use this cmdlet:

  • Display the progress with text only.
## - Sample where item counts exceeds 100:
:
Write-Progress -Activity 'List All PDF Documents' -Status "File Count: [$i]";
:
  • Display the progress bar in increment.
## - Sample where item counts is less than or equal 100:
:
Write-Progress -Id 2 -Activity "Total Entries Found: $($EventLog.Count)" -PercentComplete (($EventCount/$TotalEventLog.Count)*100) -Status 'Build PSObject Results';
:

The item count range is a limiting factor, whether there are 10 items or 1000. You will need to calculate the percentage before calling the cmdlet.  Or we could use the “-Status” parameter with a text string to display the count of the items in the task.

Calculating Percent

When calculating the percent use the “-PercentCompletion” parameter to display the progress bar. Also, make sure the percent expression is enclosed in parentheses or save the variable.

... -PercentComplete (($index/$totalcount)*100)

Warning: Be careful of reaching $totalcount = 0 in the percent calculation as this will result in a “divided by zero” exception. 

Practical Scenarios

Here are three non-GUI sample script scenarios for using Write-Progress:

  • Simple Script: List PDF Documents—to display activity with text.
  • Task Function: Getting Application Newest Log Entries—to display activity with the use of percent.
  • Multi-Task Write-Progress sample—to display progress activity on multiple task script.

These sample scenarios are just a starting point for using the Write-Progress cmdlet in a non-GUI script solution.

Simple Script: List PDF Documents

In this scenario the script will search for PDF documents. While listing the documents it will display the task progress with text.

Because the count of documents could exceed 100, it is not suitable to use the progress bar. This is why using the “-Status” with a text string can visually display the progress of the task by adding the incremental value from the for-loop $i variable.

Sample Code:

Clear-Host;

## - Get all PDF's found in Documents user folder:
$MyPDFList = Get-ChildItem -Path $env:USERPROFILE\Documents\*.pdf -File -Recurse;

## - For each to go thru each of the files found:
for ($i = 0; $i -le $MyPDFList.Count; $i++)
{

## - List all PDF fullName:
$MyPDFList[$i].FullName;
Write-Progress -Activity 'List All PDF Documents' -Status "File Count: [$i]";

## - Slowing down process:
Start-Sleep -Milliseconds 120;
};

The code uses the $MyPDFList.Count PSObject to get the total PDF documents found. In the For-loop it will increment the $i counter until it reaches the total count of documents.

This incremental value is updated in the status text string while the filename is been listed on screen.

Download Write Progress sample script: WriteProgressSample01.zip

Task Function: Getting Application Newest Log Entries

In this scenario, a script function is used to query information from the Application Event Log for the purpose of limiting the number of log entries been extracted for review.

Again, we calculate the percent in the “-PercentComplete” parameter.

... -PercentComplete (($EntryCount/$TotalEventLog.Count)*100)

The script will display the progress activity after extracting the information from the Application log. Then it will build the PowerShell PSObject variable result that will be displayed in the console.

  • Write-Progress Identifier: $Id – Unique Id for each individual progress bar.
  • Total number of event entries: $EventLog.Count – Use in the “-PercentCompleted” parameter calculation.
  • Use individual Entry count: $EntryCount – Use in the “-PercentCompleted” parameter calculation.
  • Display Total of Entries found : $EventLog.Count – Use in the “-Activity” parameter.
  • Display increment count of Entries: $EntryCount – Use in the “-Status” parameter.
  • Percent Complete calculation: (($EntryCount/$EventLog.Count)*100)

Sample Code:

function Get-MyApplicationEvent
{[CmdletBinding()]
Param (
[string]$COMPUTERNAME = "$env:COMPUTERNAME",
[ValidateRange(1,100)]
[int]$NumEntries = '100'
)
		
Write-Host "Getting Application Events in Progress";
Start-Sleep -Seconds 2;

## - Get Envent log entries with the assigned number of entries less than 101:
$EventLog = Get-EventLog -LogName Application -Newest $NumEntries -ComputerName $ComputerName;

## - Write-Progress distiguished ID:
$Id = 1;

## - Initialize variables:
$EntryCount = 0; $AppLogEntries = $null;

## - Foreach-loop to collect EventLog entries and build the PSObject variable:
$EventLog | foreach {
[System.Array]$AppLogEntries += $_ | `
Select-Object @{ label = 'Computer'; Expression = { $ComputerName } }, `
Source, `
TimeGenerated, `
Message, `
InstanceId;

## - Display Progress Bar:
Write-Progress -Id $Id `
-Activity "Total Entries Found: $($EventLog.Count)" `
-PercentComplete (($EntryCount/$EventLog.Count)*100)`
-Status "Build PSObject Results - [$($EntryCount)]";

## - Add delay to show progress bar:
Start-Sleep -Milliseconds 100;

## - Increment entry counter variable:
$EntryCount++
};

## - Display Eventlog collected entries stored in the PSObject variable:
$AppLogEntries;
}; Get-MyApplicationEvent

In either the for-loop or foreach-loop, the count increments until it reached the 100 limit.

Download Write Progress sample script: WriteProgressSample02.zip

Multi-Task Write-Progress sample

This scenario shows a series of tasks to be performed on a number of computers. It queries multiple WMI sources and builds a custom PowerShell PSObject variable.

In order to display the task activity for each of the areas collecting the information, the following needs to be determined:

  • Write-Progress Identifier: $Id – Unique Id for each individual progress bar.
  • Total number of steps: $TotalSteps – Use in the “-PercentCompleted” parameter calculation.
  • Use individual Step Number identifier: $Step – Use in the “-PercentCompleted” parameter calculation.
  • Step text description: $StepText – Use in the “-Activity” parameter.
  • Task text description: $Task – Use in the “-CurrentOperation” parameter.
  • Status text description: $StatusText – Use in the “-Status” parameter.
  • Percent Complete calculation: (($Step / $TotalSteps) * 100)

Sample Code:

Clear-Host;

## - Initializing variables:
$Svrlist = @($env:COMPUTERNAME, $env:COMPUTERNAME);
$TotalSteps = 4;
$Id = 1;
$loopCnt = 1;

## - Foreach to process all tasks:
$SysInfoResults = foreach ($name in $SvrList)
{

## ------------- Step 1 Settings -------------------------
$Step = 1;
$Task = "Begin Collecting Information";
$StepText = "[$($loopCnt)] - Collecting Information for Server: $($name)";
$StatusText = "Start Process!";

## - Display Text only progress:
Write-Progress -Id $Id -Activity $StepText -CurrentOperation $Task -Status $StatusText;

## - Slight delay:
Start-Sleep -Seconds 10;

## ------------- Step 2 Settings -------------------------
$Step = 2;
$StepText = "--> [$($loopCnt)] $($name) - Getting OS";
$Task = "Extracting Server OS Information";
$StatusText = "Step $($Step) In Process";

## - Run WMI Win32_OperatingSystem command:
$x = get-wmiObject -Class Win32_OperatingSystem -Computername $name `
-ErrorAction 'SilentlyContinue' -ErrorVariable MyError;

## - Display Progress Bar:
Write-Progress -Id $Id `
-Activity $StepText `
-Status $StatusText `
-CurrentOperation $Task `
-PercentComplete (($Step / $TotalSteps) * 100);

## - Slight delay:
Start-Sleep -Seconds 10;

## ------------- Step 3 Settings -------------------------
$Step = 3;
$StepText = "--> [$($loopCnt)] $($name) - Getting Computer";
$Task = "Extracting Computer Information";
$StatusText = "Step $($Step) In Process";

## - Run WMI Win32_ComputerSystem command:
$x1 = Get-WmiObject -Class Win32_ComputerSystem -Computername $name `
-ErrorAction 'SilentlyContinue' -ErrorVariable MyError;

## - Display Progress Bar:
Write-Progress -Id $Id `
-Activity $StepText `
-Status $StatusText `
-CurrentOperation $Task `
-PercentComplete (($Step / $TotalSteps)*100);

## - Slight delay:
Start-Sleep -Seconds 10;

## ------------- Step 4 Settings -------------------------
$Step = 4;
$StepText = "--> [$($loopCnt)] $($name) - Create PSObject";
$Task = "Build Computer PSObject";
$StatusText = "Step $($Step) In Process";

## - Build Computer system info PSObject:
[PSCustomObject]$SystemInfo = New-Object PSObject -Property @{
CntID = $Id;
ComputerName = $x.csname;
OS = $x.Name.Split('|')[0];
LastBootUpTime = $x.ConvertToDateTime($x.LastBootUpTime);
Status = "Success";
Manufacturer = $x1.manufacturer;
SystemModel = $x1.model;
'PhysicalMemory(GB)' = [float]($x1.TotalPhysicalMemory/1gb);
}; $SystemInfo

## - Display Progress Bar:
Write-Progress -Id $Id `
-Activity $StepText `
-Status $StatusText `
-CurrentOperation $Task `
-PercentComplete (($Step / $TotalSteps) * 100);

## - Slight delay:
Start-Sleep -Seconds 10;

## - Increment loop counter variable:
$loopCnt++

};

## - Display results in Console:
$SysInfoResults

This is a simple top-down programming approach where a series of tasks are executed inside the foreach-loop, one task at the time for each computer in the $Svrlist array variable.

At the end of the process, the results are stored in the $SysInfoResults PSObject variable and will be displayed in the console.

 

Download Write Progress sample script: WriteProgressSample03.zip

Summary

The ability to determine the best use of the Write-Progress cmdlet depends on whether you are going to display the progress using percent completion, or using text.

Be creative using both the “-Activity” and “-Status” parameters for displaying progress information. Try to keep it simple!

Related Articles

Feedback

As always, if you have any ideas, comments, or feedback, please visit our feedback forum and reference this post.

Max Trinidad is a Technology Evangelist at SAPIEN Technologies Inc., and a Microsoft PowerShell MVP. You can reach him at maxt@sapien.com