Select-PropertyValue

Because PowerShell is all about the objects, I often point out to beginners to use the Select-Object cmdlet to see all the properties of an object and their values.

PS C:\> get-process powershell | select *

Using Get-Member is helpful to discover property names but sometimes you need to see a value to know which property or properties you really want to work with. That’s where my Select-Object suggestion comes in. Unfortunately not every property is defined for every object so you can end up with a lot of empty properties. What I wanted was a way to only display properties for an object that have a value. Here’s my solution.

I wrote a filtering function called Select-PropertyValue.

Filter Select-PropertyValue {
    Param([switch]$NoWMISystem,[switch]$debug)
 
    if ($debug) {
        $debugPreference="Continue"
     }
 
    Write-Debug "In process"
    if (-Not $properties) {
        Write-Debug "Creating property list"
        #get properties for the pipelined object sorted by property name
        $properties=$_ | Get-Member -membertype Properties | sort Name
        
        #filter out WMI System properties if -NoWMISystem
        if ($NoWMISystem) {
            Write-Debug "Filtering out WMI System properties"
            $properties=$properties | where {$_.name -notlike "__*"}
        }
        
        Write-Debug "Found $($properties.count) properties"
    }
    
     #create an empty custom object
     Write-Debug "Creating empty object"
     $obj=New-Object PSObject
    
    #enumerate the list of properties
    foreach ($property in $properties) { 
        Write-Debug "Checking $($property.name)"
         #if object has a value for the current property
         if ($_.($property.name)) {       
            Write-Debug "found value $($_.($property.name))"
            
            #assign properties
            $obj | Add-Member -MemberType Noteproperty -name $property.Name -value ($_.($property.name))
                 
         } #end If
    } #end ForEach
    
    #write the custom object to the pipeline
    write $obj
 
} #end  function

This function is designed to accept pipelined input. Normally you would write such a function with Begin, Process and End scriptblocks. However if you don’t use the Begin and End scriptblocks, you can create a filter as I’ve done. The filtering function’s code is what would be found in the Process scriptblock.

The function can also take two parameters, -debug and -NoWMISystem. The former will enable debug messages throughout the function. I inserted Write-Debug commands throughout the function so I could track what was happening. Using -debug allows me to see the output of those commands. The second parameter will suppress the system classes associated with a WMI object such as __RELPATH and __GENUS. I rarely need to see this information.

To use the function I first dot source the ps1 file as it also creates an alias for the function, spv. Now I can run command like this:

PS C:\> gwmi win32_bios | spv -noWMISystem

The WIn32_Bios object is piped to the function. The first step the function takes is to build a variable with all of the property names for the pipelined object.

if (-Not $properties) {
        Write-Debug "Creating property list"
        #get properties for the pipelined object sorted by property name
        $properties=$_ | Get-Member -membertype Properties | sort Name

These are stored in a variable, $properties, that I only need to create once. It $properties already exists the function skips this step.

If I’ve specified -nowmisystem, then $properties is filtered further to remove the system classes.

#filter out WMI System properties if -NoWMISystem
 if ($NoWMISystem) {
     Write-Debug "Filtering out WMI System properties"
     $properties=$properties | where {$_.name -notlike "__*"}
 }

The function is going to create a new custom object and copy the properties that have values so it first creates an empty object.

#create an empty custom object
Write-Debug "Creating empty object"
$obj=New-Object PSObject

The $properties variable is enumerated one at a time. If the property of the pipelined object has a value, then the property name and value are added to the custom object.

#enumerate the list of properties
foreach ($property in $properties) { 
    Write-Debug "Checking $($property.name)"
     #if object has a value for the current property
     if ($_.($property.name)) {       
        Write-Debug "found value $($_.($property.name))"
        
        #assign properties
        $obj | Add-Member -MemberType Noteproperty -name $property.Name -value ($_.($property.name))
             
     } #end If
} #end ForEach

After all the properties have been checked the object is written to the pipeline.

#write the custom object to the pipeline
write $obj

This is now more useful to me:

PS C:\> gwmi win32_bios | spv -n

BiosCharacteristics  : {7, 8, 9, 11…}
BIOSVersion          : {FUJ    – 1180000}
Caption              : Default System BIOS
CurrentLanguage      : enUS
Description          : Default System BIOS
InstallableLanguages : 2
ListOfLanguages      : {enUS, jaJP}
Manufacturer         : FUJITSU // Phoenix Technologies Ltd.
Name                 : Default System BIOS
PrimaryBIOS          : True
ReleaseDate          : 20080602000000.000000+000
SerialNumber         : R6Y02331
SMBIOSBIOSVersion    : Version 1.18
SMBIOSMajorVersion   : 2
SMBIOSMinorVersion   : 4
SMBIOSPresent        : True
SoftwareElementID    : Default System BIOS
SoftwareElementState : 3
Status               : OK
Version              : FUJ    – 1180000

The function writes to the pipeline so I can pipe it to other cmdlets as well. This especially useful when you want to export as you’ll end up only exporting properties with values.

PS C:\> gwmi win32_service | spv -n | export-csv c:\test\servicedata.csv -notype

Download the script file with this function here.

Enjoy and if you extend functionality, I hope you’ll let me know.