Hey, Scripting Guy! Question

Hey, Scripting Guy! How can I use Windows PowerShell to locate all the .wma files on my computer?

-- LC

SpacerHey, Scripting Guy! AnswerScript Center

Hey, LC. Harry Potter. Hogwarts. Ron Weasley. Dumbledore. Quiddich.

What’s all that about? Oh, nothing: just a few words that idly floated through the head of the Scripting Guy who writes this column. And before you ask, no, those words are not a less-than-subtle jab at the Scripting Editor, with whom he recently had a … difference of opinion … a difference of opinion revolving, in part, around these fictional constructs. As you might expect, the Scripting Guy who writes this column has nothing but the utmost respect for the Scripting Editor and for her opinions.

Even if she is a bit of a Muggle.

So, no, there’s no hidden agenda here. If anything, the Scripting Guy who writes this column mentioned Harry Potter simply because Harry and his friends are well acquainted with magic, and the Scripting Guy likes to think that the answer to your question is equally magical:

Get-WMIObject Win32_LogicalDisk -filter "DriveType = 3" | 
Select-Object DeviceID | 
ForEach-Object {Get-Childitem ($_.DeviceID + "\") -include *.wma -recurse}

Note. Of course she’ll fall for that; why wouldn’t she?

As you’ve no doubt heard, Windows PowerShell is a new command shell/scripting language released by the good folks here at Microsoft. (If you haven’t heard about Windows PowerShell, you soon will; in fact, one of these days we’ll have an important announcement right here in the Script Center.) Windows PowerShell is an interesting technology, and it has some definite strengths. Does that make it “better” than VBScript? That’s like asking whether vanilla is better than chocolate: it depends on who you are and what you like. Either way, however, there’s no doubt that Windows PowerShell can easily solve this particular problem, and with a minimal amount of effort.

Note. For the record, chocolate is better than vanilla. Of course, the Scripting Editor will probably opt for flavorless gelatin as her favorite. But that’s OK; she’s fully-entitled to her opinion.

Even if it is completely wrong.

Let’s get down to business here. In order to locate all the .wma files on a computer we need to do three things:

Obtain a list of all the hard drives installed on the computer.

Grab the drive letter for each of these drives.

Search each drive for all the .wma files.

With that in mind, why don’t we start with step 1: obtaining a list of all the hard drives installed on the computer. (By the way, even though this looks like a couple different lines, the entire command should be typed on the same line.)

Although there are a couple different ways we could retrieve the list of hard drives we decided to use WMI and the GetWMIObject cmdlet; that’s not only an approach that we knew would work, but also an approach that should be reassuringly-familiar to those of you who are proficient in VBScript but are new to Windows PowerShell. Here’s the command that retrieves the list of disk drives found on the local computer:

Get-WMIObject Win32_LogicalDisk -filter "DriveType = 3"

As you can see, there’s nothing too terribly difficult here. To begin with, we simply call the Get-WMIObject cmdlet, followed by the name of the WMI class we want to connect to (Win32_LogicalDisk). We then add the –filter parameter, which is pretty much a WMI Select * From query minus the Select * From and the word Where. In VBScript we’d use a query that looks like this:

"Select * From Win32_LogicalDisk Where DriveType = 3”

In Windows PowerShell we use a filter that looks like this:

"DriveType = 3"

See? Nothing even remotely difficult about that.

When we run this command we get back a collection consisting of all the properties of all the hard disks on the computer (any logical disk with a DriveType of 3 is a hard disk). That’s great, except that all we really want is the drive letter for each of these hard disks. Therefore, we “pipe” this collection to the Select-Object cmdlet (the | symbol represents the pipeline). What does that mean? That means we use Get-WMIObject to grab the collection, but instead of doing anything further using Get-WMIObject we hand the collection over to Select-Object. We then use that cmdlet to select DeviceID, the only property we care about:

Select-Object DeviceID

We still have a collection consisting of all the hard drives on the computer; it’s just that we’ve removed all the property values except for DeviceID. That leaves us with a collection similar to this:

C:
D:
E:

What we need to do now is pipe that collection once more, this time handing the trimmed-down collection to the ForEach-Object cmdlet. The ForEach-Object cmdlet will then loop through the set of hard disks, using the Get-ChildItem cmdlet against each drive in the collection. Get-ChildItem, as the name implies, is a cmdlet that can retrieve all the items found in a specified container, including all the files and subfolders in a folder.

Here’s what that command looks like:

ForEach-Object {Get-Childitem ($_.DeviceID + "\") -include *.wma -recurse}

In other words, for each drive we call Get-ChildItem, binding the cmdlet to the root folder (for example, C:\). In order to specify the root folder we use $_.DeviceID followed by a \. In case you’re wondering, in Windows PowerShell $_ is a special variable that represents the object currently in use; the first time through the loop, that means that $_.DeviceID will be C:, the DeviceID property for item one in the collection.

In addition we pass Get-ChildItem a pair of parameters:

-include *.wma. This is a filter parameter that restricts returned data to files with a .wma file extension.

-recurse. This parameter causes Get-ChildItem to automatically search all the subfolders of the target folder; because the target folder is C:\ that will result in the cmdlet searching all the folders on drive C.

Note. Yes, in order to search all the folders on drive C: all you have to do is add the –recurse parameter; there’s no need to write a recursive subroutine like you do when using WMI or the FileSystemObject. Hmmm, maybe this is magic after all!

After we run Get-ChildItem on drive C we then loop around and do the same thing with the next drive in the collection. Just exactly the same way a For Each loop works in VBScript. (The syntax is different, but the concept is the same.)

By the way, you should find that this script runs pretty quickly. Give it a try and see for yourself.

Now, where were we? Oh, that’s right: Hagrid. Gryffindor. Voldemort. Hermione Granger.

Note. Yes, we don’t doubt that the Scripting Editor has a few words “randomly” floating through her head right about now. But as the Scripting Editor she has to censor things like that and can’t say what she really wants to say, at least not here on TechNet. Take that, Scripting Editor!

Editor’s Note. Yes, the Scripting Editor does have plenty to say to the Scripting Guy that can’t go on TechNet. But here are some things that can: Lawsuit; lawyers; copyright; trademark; unemployment; bankruptcy; living out of your car; living out of the Scripting Son’s car. (We finally hit on a really scary one didn’t we?)