Summary: Microsoft Scripting Guy, Ed Wilson, shows you how to use Windows PowerShell to clean up your WMI data output.
Microsoft Scripting Guy, Ed Wilson, is here. So far the Scripting Wife has been scarce. She got up before sunrise (I believe) and headed out to points unknown. Actually, I believe she had scheduled a “chicks breakfast” with her friends who are hanging out down here in Myrtle Beach, South Carolina (and no, it was not a PowerShell Chicks User Group breakfast). But that is OK, because it means that I get to fend for myself. And with a beach, ocean, and tons of cool things to do within walking distance, fending for one’s self was never easier. There is even wifi on the beach—the only trick is to avoid getting sand in one’s laptop.
The problem with extra “stuff” in a WMI query
The problem of getting “extra stuff” back when you do a WMI data query has been around since Windows PowerShell 1.0 (even longer if you count the beta period). But to be honest, it is not that you get extra stuff back when you perform a WMI query, it is that certain WMI classes have a default output that is configured via the types.ps1xml file. For example, the image that follows illustrates the portion that shows the format XML information for the Win32_BIOS WMI class.
When you perform a basic WMI query, the default display formats the output. This is because the basic query (no specifically requested properties) matches the System.Management.ManagementObject#root\cimv2\Win32_BIOS type that is specified in the format XML file. This information is shown here:
PS C:\> gwmi win32_bios | gm | select typename -Unique
TypeName
--------
System.Management.ManagementObject#root\cimv2\Win32_BIOS
When I use the Property parameter or I perform a custom WQL query, the returned type changes; and therefore, it does not match the type that is defined in the types.ps1xml file, as shown here.
PS C:\> gwmi win32_bios -property name | gm | select typename -Unique
TypeName
--------
System.Management.ManagementObject#\Win32_BIOS
PS C:\> gwmi -q "select name from win32_bios" | gm | select typename -u
TypeName
--------
System.Management.ManagementObject#\Win32_BIOS
Filtering out the system properties
So what is the “extra stuff” that comes back when a custom WMI query runs? They are system properties. One of the nice things about WMI is that all of the system properties begin with a double underscore ( __ ). In addition, all system WMI classes begin with a double underscore. When you run a WMI query that selects only the name property, a whole bunch of system properties also return. This is shown here:
PS C:\> gwmi -q "select name from win32_bios"
__GENUS : 2
__CLASS : Win32_BIOS
__SUPERCLASS :
__DYNASTY :
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
Name : Default System BIOS
One way to clean up the output is to filter these properties by using a range in either Format-Table or Format-List as shown here:
PS C:\> gwmi -q "select name from win32_bios" | fl [a-zA-Z]*
Name : Default System BIOS
Scope : System.Management.ManagementScope
Path :
Options : System.Management.ObjectGetOptions
ClassPath : Win32_BIOS
Properties : {Name}
SystemProperties : {__GENUS, __CLASS, __SUPERCLASS, __DYNASTY...}
Qualifiers : {dynamic, Locale, provider, UUID}
Site :
Container :
PS C:\> gwmi -q "select name from win32_bios" | ft [a-zA-Z]*
Name Scope Path Options ClassPat Propert SystemP Qualifi Site Contain
h ies roperti ers er
es
---- ----- ---- ------- -------- ------- ------- ------- ---- -------
Defau... Syste... Syste... Win32... {Name} {__G... {dyn...
Unfortunately, in Windows PowerShell 2.0, that filter trick quit working because no sooner do you get rid of one group of system properties, than another group of system properties appears. It does not matter, whether you use the Format-Table cmdlet or if you use the Format-List cmdlet—the extra system properties appear to be here to stay.
Using Format-Table or Format-List
If you specifically choose the properties in your WMI query, you also need to specifically choose them in the Format-Table or Format-List query. This technique is shown here:
PS C:\> gwmi -Property name -Class win32_bios | ft name
name
----
Default System BIOS
PS C:\> gwmi -Property name -Class win32_bios | fl name
name : Default System BIOS
There are at least two things “wrong” with this approach. The first is that once you use a Format-Table, Format-List, or Format-Wide cmdlet, you cannot do anything else with your pipeline because the object is now gone. In other words, the format* cmdlets destroy the pipeline. What is the difference? Well, it can be seen by examining the object in the pipeline.
In the first example, we see that we have a win32_bios management object.
PS C:\> gwmi -Property name -Class win32_bios | gm | select typename -u
TypeName
--------
System.Management.ManagementObject#\Win32_BIOS
But following the pipeline to the Format-List cmdlet, we no longer have a management object at all. Instead they are all format-related objects as shown here.
PS C:\> gwmi -Property name -Class win32_bios | fl name | gm | select typename -
TypeName
--------
Microsoft.PowerShell.Commands.Internal.Format.FormatStartData
Microsoft.PowerShell.Commands.Internal.Format.GroupStartData
Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData
Microsoft.PowerShell.Commands.Internal.Format.GroupEndData
Microsoft.PowerShell.Commands.Internal.Format.FormatEndData
Remove the “extra stuff” and retain the object
The best way to remove the “extra stuff” is to use the Select-Object cmdlet. This cmdlet removes the “stuff” and retains the object-oriented nature of the data. This means that you can do other things with it. In the command that follows, the Name and the ProcessID properties from the Win32_Service WMI class are chosen from the WMI data. Next, the Select-Object cmdlet chooses the same properties. Now they are sent to Get-Member, and the typename is displayed. This is shown here:
PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processID
| gm | select typename -u
TypeName
--------
Selected.System.Management.ManagementObject
Now, what does this look like when it is displayed to the Windows PowerShell console? The following is a partial output (note that there are no system properties).
PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processID
name processID
---- ---------
AdobeActiveFileMonitor6.0 2036
AdobeARMservice 1776
AeLookupSvc 0
ALG 0
AppIDSvc 0
Appinfo 504
AppMgmt 0
aspnet_state 0
AudioEndpointBuilder 456
AudioSrv 336
AxInstSV 0
BDESVC 0
BFE 1920
BITS 504
Browser 0
bthserv 2796
But what about the object itself? Look at what Get-Member displays. We have created a custom management object with two properties: Name and ProcessID.
PS C:\> gwmi -Property name, processID -Class win32_Service | Select name, processId
| gm
TypeName: Selected.System.Management.ManagementObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
name NoteProperty System.String name=AdobeActiveFileMonitor6.0
processId NoteProperty System.UInt32 processId=2036
Because this is still a management object, we can continue to use Windows PowerShell to massage the data. As shown here, we can use the Sort-Object cmdlet to sort the data, and we can use the –Last parameter to choose the last two services.
PS C:\> gwmi -Property name, processID -Class win32_Service | sort name | Select name
, processId -Last 2
name processId
---- ---------
wudfsvc 456
WwanSvc 0
One thing that can simplify things is to list the properties that you are interested in obtaining in an array, and then use them directly in your Get-WmiObject and your Select-Object queries. In this way, you only need to type them once. Because I do not like typing quotation marks, I create a single string with the CSV list of property names. Then I use the Split operator to create my array. The thing to keep in mind is to not put spaces between the property names. This code is shown here:
$property = "Name,started,StartName" -split ","
gwmi -p $property -cl win32_service | select $property
When you have the properties in an array, you can use them as often as you want without the need to retype them. In the example that follows, the Format-Table cmdlet is used to display a table of service information.
$property = "Name,started,StartName" -split ","
gwmi -p $property -cl win32_service | select $property | ft $property –AutoSize
The use of the commands and their associated output is shown here:
Well, that is about it for right now. The Scripting Wife will be back soon (I imagine), and then she was talking about going to do something. No idea what—just something. But hey, we are at the beach, so there is bound to be something going on. Maybe we can found the Myrtle Beach Windows PowerShell Users Group while we are down here.
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