how to filter name/value pairs under a registry key by name and value in PowerShell?
A two part answer.
We start with the $key
from the registry:
$path = 'hkcu:\Software\Microsoft\Windows\CurrentVersion\Extensions'
$key = Get-Item $path
$key
$key | Get-Member
Since $key
is a Microsoft.Win32.RegistryKey, you cannot get the name value pairs out directly.
The first step is to create a list of PSCustomObjects with Name
/Value
pairs. The Name
comes from the GetValueNames piped through a ForEach-Object. For each of those Names, we get the Value
through GetValue:
$namevalues = $key.GetValueNames() |
ForEach-Object {
[PSCustomObject] @{
Name = $_;
Value = $key.GetValue($_)
}
}
$namevalues | Format-Table
An alternative for the first step is to use the Select-Object using -ExpandProperty as explained by Scott Saad:
$namevalues = $key | Select-Object -ExpandProperty Property |
ForEach-Object {
[PSCustomObject] @{
Name = $_;
Value = $key.GetValue($_)
}
}
$namevalues | Format-Table
The second step is to filter the $namevalues
either by Name
or by Value
.
The Where-Object has some pretty cool Comparison operators that accept regular expressions like match
, notMatch
, etc.
To make the code more readable, you can wrap lines (thanks Joey!) either use the backtick (`) or take advantage of the places in the PowerShell syntax where it does accept line breaks, like after a pipe (|) or opening brace ({):
$matches = $namevalues |
Where-Object {
$_.Name -match '^xls' `
-or $_.Value -match 'msaccess.exe$'
}
$matches | Format-Table
The result is as wanted:
Name Value
---- -----
xlsx C:\PROGRA~2\MICROS~1\Office15\EXCEL.EXE
xls C:\PROGRA~2\MICROS~1\Office15\EXCEL.EXE
mdb C:\PROGRA~2\MICROS~1\Office15\MSACCESS.EXE
mda C:\PROGRA~2\MICROS~1\Office15\MSACCESS.EXE
There's a much more clever way to enumerate registry values (found it here). And it's more powershell-way IMO.
I've turned it into a one-liner:
(Get-ItemProperty $path).psobject.properties |
where {$_.name -like "xls*" -or $_.value -like "*\MSACCESS.EXE"} |
select name,value
Update: As noted in comments by @mklement0, you should be careful about properties PSPath
, PSParentPath
, PSChildName
, PSDrive
, and PSProvider
within the psobject.properties
.
A more robust PSv4+ alternative to montonero's helpful answer:[1]
Get-Item $path -PipelineVariable key | ForEach-Object Property | ForEach-Object {
$name = ($_, '')[$_ -eq '(default)'] # translate '(default)' to '' for API calls
if ($name -like 'xls*' -or ($value = $key.GetValue($name)) -like "*\MSACCESS.EXE")
{ [pscustomobject] @{ Name = $name; Value = $value } }
}
-PipelineVariable key
stores the[Microsoft.Win32.RegistryKey]
instance returned byGet-Item
in variable$key
for later use in the pipeline.ForEach-Object Property
enumerates the target key's value names (via the.Property
note property that PowerShell adds to the output[Microsoft.Win32.RegistryKey]
instance).Inside the
Where-Object
script block,$_
then refers to the value name at hand, and$key.GetValue(<valueName>)
is used to retrieve the associated data.- Important: In the
.Property
array, PowerShell translates the default value name, which is the empty string (''
) at the API level, into name'(default)'
; thus, if$_
is'(default)'
, you must translate it to''
before calling$_.GetValue(<valueName>)
, which is what($_, '')[$_ -eq '(default)']
does.
- Important: In the
[pscustomobject] @{ ... }
then constructs and outputs a[pscustomobject]
instance with.Name
and.Value
properties reflecting the matching value's name and data.
[1] montonero's answer is concise and works well in the case at hand, but it comes with caveats:
PowerShell's registry provider automatically adds the following additional note properties (members of type NoteProperty
, as reflected in the output from Get-Member
) containing metadata about the targeted registry keys to the [pscustomobject]
instance that Get-ItemProperty
outputs:
PSPath
,PSParentPath
,PSChildName
,PSDrive
,PSProvider
These can interfere with filtering based on .psobject.properties
in two ways:
Using wildcard matching against
$_.Name
can accidentally include these properties.- E.g.,
$_.Name -like '*drive*'
would match thePSDrive
property, even though it isn't actually part of the registry key.
- E.g.,
Perhaps more hypothetically, if the registry key happens to have values of the same name as these provider properties, the provider properties shadow (override) these values.
- E.g., if the key has a
PSPath
value,$_.Value
will report the provider property value instead.
- E.g., if the key has a