Windows PowerShell Tip of the Week

Here’s a quick tip on working with Windows PowerShell. These are published every week for as long as we can come up with new tips. If you have a tip you’d like us to share or a question about how to do something, let us know.

Find more tips in the Windows PowerShell Tip of the Week archive.

Making Progress

One of the nice things about scripting (and working at the Windows PowerShell console) is that most tasks complete about as fast as you can call the script. Need information about all the services on a computer? Well, using Windows PowerShell, type the following and then press ENTER:

Get-Service

No sooner will you press ENTER than service information will begin to fill the screen, a process that will take maybe one whole second before it finishes. It’s nice that you can retrieve service information that quickly; it’s also nice that, as a script writer, you don’t need to provide any sort of progress bar to reassure the user that progress is being made.

Of course, not all your scripts – or all your commands – are going to run that quickly; sooner or later you’re going to encounter an operation that takes 30 seconds (or more) to complete. In a case like that it’s always nice to give your user some sort of feedback to let them know: 1) that something really is happening (that is, the script isn’t simply hung up); and, 2) progress is being made. That’s a nice thing to do, except for this: how much trouble are you going to have to go through to display a progress bar while this lengthy activity takes place?

Well, as it turns out, not much trouble at all:

$colFiles = Get-ChildItem C:\Windows -include *.dll -recurse

foreach ($objFile in $colFiles)
    {
        $i++
        $intSize = $intSize + $objFile.Length
        Write-Progress -activity "Adding File Sizes" -status "Percent added: " -PercentComplete (($i / $colFiles.length)  * 100)

    }

$intSize = "{0:N0}" -f $intSize

Write-Host "Total size of .DLL files: $intSize bytes."

As we noted earlier, there’s no reason to include a progress bar in your script unless you have an activity that takes a little bit of time to complete; it’s pointless to have a progress bar pop up onscreen and then almost immediately disappear. The task we’ve chosen for our sample script is this: we’re going to retrieve all the .DLL files from the Windows folder and all of its subfolders; add together the sizes of each of these files; and then report back the total size of all the .DLL files. That doesn’t take a long time to complete, but long enough to make a progress bar worthwhile.

In order to do that, the first thing we need to do is retrieve a collection of all the .DLL files; that’s what this line of code is for:

$colFiles = Get-ChildItem C:\Windows -include *.dll -recurse

We should mention that this activity – retrieving all the .DLL files in the Windows folder and its subfolders – takes a few seconds to complete on its own. However, we don’t display a progress bar while the files are retrieved. Why not? For one simple reason: we can’t. Progress bars need to be displayed – and updated – inside a loop of some kind; that’s because we need to tell the progress bar when to update. For better or worse, the progress bar can’t just pop up during the processing of a single command (like a call to Get-ChildItem) and then update itself.

Once we retrieve all the .DLL files we can set up a foreach loop that loops through each and every item in that collection. What are we going to do inside that loop? Well, for starters, we’re going to increment a counter variable named $i by 1:

$i++

Because we haven’t referenced $i yet, it starts off equal to 0; incrementing 0 by 1 thus makes $i equal to 1. Interestingly enough, the value 1 also represents the number of times we’ve run through the foreach loop. Coincidence? We don’t think so ….

After updating our counter variable we then add the size (Length) of the first file in the collection to another counter variable, this one named $intSize:

$intSize = $intSize + $objFile.Length

At this point we’ve made some progress; that means it’s time to update our progress bar. That’s what this line of code is for:

Write-Progress -activity "Adding File Sizes" -status "Percent added: " -percentComplete (($i / $colFiles.length)  * 100)

So what’s going on here? Well, to begin with, we’re using the Write-Progress cmdlet to display a progress bar onscreen. We’re also passing Write-Progress three parameters:

  • -activity. This represents the caption for our progress bar. (You’ll see what we mean by that in just a moment.) We’re adding together file sizes so we set the activity to Adding File Sizes.

  • -status. The status describes the current state of the activity. In this case, the status will represent the percentage of the task that has been completed. For this script we simply assign status the string Percent added:.

  • -percentComplete. This is, well, the percentage of the task that has been completed. For this script it’s easy to calculate the percent completed; that’s because we know exactly how many files we have in the collection. (How? Because the Length property of the collection $colFiles tells us how many files are in the collection.) To determine the percent completed we take the number of files we’ve already worked with (the counter variable $i) divided by the total number of files ($colFiles.length). We then multiply that value by 100.

And then, after we update the progress bar, we return to the top of the loop and repeat the process with the next file in the collection. As this all takes place our screen will look something like this:

It’s not the Mona Lisa or the Venus de Milo, but it’s not bad. And it does a much better job of tracking script progress than either the Mona Lisa or the Venus de Milo. (The Venus de Milo doesn’t even have any fingers to count with.)

That should get you started with the Write-Progress cmdlet and PowerShell’s built-in progress bar. For more information, take a peek at the PowerShell help files:

Get-Help Write-Progress -full

As for the Scripting Guys, the percentage complete for our progress just happens to be 100% (for this week anyway). We’ll see everyone again next week.