Wednesday, April 24, 2013

Working with Collections in PowerShell


Since PowerShell is based on the .Net Framework and incorporates various other technologies like WMI and CIM, there is always more than one way to accomplish the same thing. Come join us for this short post where we learn how to choose the best method to accomplish our tasks.

Using Batch Cmdlets

Earlier on in the series, when we introduced you to the pipeline, we showed you how you can take the objects that one cmdlet outputs and pass them as input to another cmdlet using something like this:
Get-Process -Name notepad | Stop-Process
This would kill any process with the name “notepad”. But you are probably wondering how PowerShell is able to kill every instance of notepad with a single command. The answer lies within the help of the Stop-Process cmdlet.
help Stop-Process –Full

If you look at the last line of code in the syntax section, you can see that the InputObject parameter accepts an object of type Process[], and whenever you see a type followed by two square brackets like that it means that the parameter accepts one or more of the preceding type. In this case, it accepts one or more process objects. Technically, we would say that the InputObject cmdlet accepts a process array. Whenever you have a cmdlet that supports batch operations in this manner, use it. This is choice number one.

Using WMI

Although WMI isn’t the best piece of technology to ship from the Microsoft HQ, it does come in at second on the list of how to work with collections of objects. We could easily get a list of running processes from the Win32_Process class like so:
Get-WmiObject –Class Win32_Process

Since the WMI query returns its own kind of object, you will need to look for a method that can stop the process, so lets pipe that to Get-Member.
Get-WmiObject –Class Win32_Process | Get-Member

Looks like the closest thing to stop is the terminate method, so that must be the one. To call a method on a WMI Object, you simply pipe it to Invoke-WmiMethod and specify the name of the method.
Get-WmiObject -Class Win32_Process -Filter “name=’notepad.exe’” | Invoke-WmiMethod -Name Terminate

Great, that did the trick. Whenever you get a ReturnValue of 0 in WMI, just remember that the command executed successfully.

Enumeration

Failing the other two methods, if you have to do something to a bunch of objects you can enumerate the entire object and act on each individual object. First you have to find the method you would use to stop a single process.
Get-Process | Get-Member -MemberType Method

Perfect, it looks like we can use the Kill method and then pipe into ForEach-Object to kill them all.
Get-Process -Name notepad | ForEach-Object -Process {$_.Kill()}

Here we took all the process objects that Get-Process returned and passed them to the ForEach-Object cmdlet. Much like the Where-Object cmdlet, we represented each object in the pipeline with $_ which we were able to call to the Kill() method on. With all that said and done, enumerating a collection is much slower than the above methods and should only be used as a last result.

Summary

Choice One
Get-Process -Name notepad | Stop-Process
Choice Two
Get-WmiObject -Class Win32_Process -Filter “name=’notepad.exe’” | Invoke-WmiMethod -Name Terminate
Choice Three
Get-Process -Name notepad | ForEach-Object -Process {$_.Kill()}
That’s all for this time folks, see you next week for more PowerShell fun.

No comments:

Post a Comment