I'm trying to get a feel for the idioms to use in PowerShell.
Given this script:
$path = 'hkcu:\Software\Microsoft\Windows\CurrentVersion\Extensions'
$key = Get-Item $path
$key
I get the output at the bottom of this question.
I'd like to get output of the properties (the name/value pairs under the $key
) where I can filter on both name and value.
For instance, filter to list all the Extensions that have:
- name like
xls*
- or value like
*\MSACCESS.EXE
Or an exclude filter: exclude all names like doc*
Fir the first filter, I'd want a result like this:
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
This is the original output of the script:
Hive: HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion
Name Property
---- --------
Extensions rtf : C:\PROGRA~2\MICROS~1\Office15\WINWORD.EXE ^.rtf
dot : C:\PROGRA~2\MICROS~1\Office15\WINWORD.EXE ^.dot
dotm : C:\PROGRA~2\MICROS~1\Office15\WINWORD.EXE ^.dotm
dotx : C:\PROGRA~2\MICROS~1\Office15\WINWORD.EXE ^.dotx
docm : C:\PROGRA~2\MICROS~1\Office15\WINWORD.EXE ^.docm
docx : C:\PROGRA~2\MICROS~1\Office15\WINWORD.EXE ^.docx
doc : C:\PROGRA~2\MICROS~1\Office15\WINWORD.EXE ^.doc
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
Edit
I solved part of the problem: getting a list of Name/Value pairs. It uses PSCustomObject:
$namevalues = $key.GetValueNames() | ForEach-Object { [pscustomobject]@{ Name=$_; Value=$key.GetValue($_) } }
$namevalues
(How should I wrap that code?)
Any help with the filtering would be much appreciated
A more robust PSv4+ alternative to montonero's helpful answer:[1]
-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..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.[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 fromGet-Member
) containing metadata about the targeted registry keys to the[pscustomobject]
instance thatGet-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.$_.Name -like '*drive*'
would match thePSDrive
property, even though it isn't actually part of the registry key.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.
PSPath
value,$_.Value
will report the provider property value instead.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:
Update: As noted in comments by @mklement0, you should be careful about properties
PSPath
,PSParentPath
,PSChildName
,PSDrive
, andPSProvider
within thepsobject.properties
.A two part answer.
We start with the
$key
from the registry: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. TheName
comes from the GetValueNames piped through a ForEach-Object. For each of those Names, we get theValue
through GetValue:An alternative for the first step is to use the Select-Object using -ExpandProperty as explained by Scott Saad:
The second step is to filter the
$namevalues
either byName
or byValue
.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 ({):
The result is as wanted: