Add a Progress Bar to Your PowerShell Script

Add a Progress Bar to Your PowerShell Script

  • Comments 9
  • Likes

 

Summary: Microsoft Scripting Guy Ed Wilson shows you how to add a progress bar to your script.

 

Microsoft Scripting Guy Ed Wilson here. I was sitting in the kitchen reflecting on how much nicer things are nowadays than they used to be even a few years ago. For example, the Scripting Wife and I have a computer in our kitchen. The computer itself is about the size of a small paperback book, and it is hooked up to a nice flat panel monitor. With a wireless keyboard, mouse, and network connection, it is a rather compact installation. Our first kitchen computer was a mini-tower, hooked up to a 15 inch monitor, and it took us several days to cut holes in dry wall, drill holes through wall studs to run category five wire from the upstairs rack to the downstairs kitchen. It was worth the effort because having a computer in the kitchen is super handy around our household. Comparing the amount of work involved, the old installation took several days, and the new installation took only a few minutes.

That same economy of effort also works for scripting progress bars. It is common to display some kind of progress when a script takes a long time to complete. When a user launches the script and nothing happens, one begins to wonder if the script launched correctly. We probably have all had the occasion to start three or four copies of a program that is slow in launching because we clicked and nothing happened. The same principle applies to scripts: if the script will take a long time to run, provide feedback to the user (or even to yourself if you run the script six months from now and you forget that it takes a long time to run.)

Five years ago, I wrote a VBScript script such as the one that follows. It collects a list of all running services, and back then it took a long time to run.

CollectRunningServicesDisplayProgress.vbs

strComputer = "."

wmiNS = "\root\cimv2"

wmiQuery = "Select name from win32_service where state = 'running'"

Set stdout = WScript.StdOut

stdout.write "Please wait"

strout = wmiQuery & VbCrLf

Set objWMIService = GetObject("winmgmts:\\" & strComputer & wmiNS)

Set colItems = objWMIService.ExecQuery(wmiQuery)

For Each objItem in colItems

stdout.Write "."

strout = strout & objItem.name & VbCrLf

Next

stdout.WriteBlankLines(1)

stdout.write(strOut)

Using the Write-Progress Windows PowerShell cmdlet, I can trim the previous VBScript from 14 lines to six lines of code. One thing that is cool about WMI queries is that the result from Get-WmiObject is a collection. That collection has a count property. Armed with the count property, I can display a progress bar that indicates a percentage of completion of the command. The ProgressDemoWMI.ps1 script is shown here.

ProgressDemoWMI.ps1

$wmiQuery = "Select name from win32_service where state = 'running'"

$colItems = Get-WmiObject -Query $wmiQuery

For($i = 1; $i -le $colItems.count; $i++)

{ Write-Progress -Activity "Gathering Services" -status "Found Service $i" `

-percentComplete ($i / $colItems.count*100)}

$colItems | Select name

When using the Write-Progress cmdlet, two parameters are required. The first is the activity parameter. The string supplied for this parameter appears on the first line in the progress dialog. The second required parameter is the status parameter. It appears under the Activity line. The dialog that appears in the following image is shown when the ProgressDemoWMI.ps1 script runs from within the Windows PowerShell ISE.

Image of dialog box that appears when PowerShell script is run in the PowerShell ISE

When the ProgressDemoWMI.ps1 script runs inside the Windows PowerShell console, a green bar appears at the top of the console, and yellow o’s track their way across the console window. This is shown in the following image.

Image of progress bar in PowerShell console

Displaying a percentage of completion is a great progress indicator if you know how many work items exist. Unfortunately, many times you have no idea how many items are in the collection. This is especially true when commands are piped, because there is no way to determine how many items need to be streamed as items are coming over the pipeline.

One solution is to store items in a variable, and then iterate over the items collected in the variable. This is illustrated in the FilesProgressDemo.ps1 script.

FilesProgressDemo.ps1

$files = Get-ChildItem -Path c:\fso

For($i = 1; $i -le $files.count; $i++)

{ Write-Progress -Activity "Collecting files" -status "Finding file $i" `

-percentComplete ($i / $files.count*100)}

$files | Select name

As you can tell, the FilesProgressDemo.ps1 script is nearly identical to the earlier script—the difference is collecting the files instead of services.

That is all there is to displaying a progress indicator.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • The problem I sometimes run into with WMI is it can take a while to finish its collection. For instance, the Win32_Product class can take some time to process. Progress isn't displayed in real time because the count property isn't populated until the collection is complete. Progress is actually counted after it's already been done and the delay kinda defeats the purpose of the progress indicator. It's just a thing with me, wish it could be while it's happening instead of after the fact.

  • Hello Scripting Guy,

    would you please be so kind to tell me how I can put up a progress bar for the creation of a sharepoint database, which I create through powershell cmdlets? Or for similar tasks like creating a site in SharePoint 2010? The creation of a SharePoint DB in PowerShell can take some time and I want to indicate a progress so someone would not think of a system freeze.

    Thank you very much

    Kind regards

  • @Marc

    Run the creation as a job.  Write a loop that polls the job every second or so and updates the progress bar.

    Post your question here for more assistance: social.technet.microsoft.com/.../threads

  • @Rich @marc one option is to use a time based progress indicator instead of a percent complete. You can then "estimate" about how long the task will take, and move your indicator from there. i talk about this in other articles.

  • You could also do something like this:

    $files = Get-ChildItem -Path c:\fso

    foreach ($file in $files)

    { Write-Progress -Activity "Collecting files" -status "Finding file $i" `

    -percentComplete ($i++ / $files.count*100)}

    Of course, that throws off the -Staus value a little bit

  • Hi everybody

    Nice article.But my question is,how can I show the progress for tasks that there is no items for counting??? .Here,the calculation is based on the NO of for example files or processes,but how about the jobs that there is no object to count...somthing like moving one virtual machine from one computer to another with powershell commands????

  • If you want to display progress when there are no items to count, try something like the following:

    while (CheckIfJobIsDone($name)) {

           $status += '.'

           Write-Progress -Activity "Waiting for some job to finish" -Status $status -PercentComplete -1

           Start-Sleep 1

    }

  • Progress bar control, http://www.kettic.com/winforms_ui/csharp_guide/track_status_progressbar.shtml

  • Progress Bar control, http://www.kettic.com/winforms_ui/csharp_guide/track_status_progressbar.shtml