PowerShell is presenting registry values (and their registry data) as an array in $\_.Property
in their parent registry key. How do I select and delete only the registry values where the value is like \*String\*
?
I have tried searching high and low on StackOverflow and on Google trying many combinations that involve Get-Item
, Get-ItemProperty
, Get-ChildItem
, Select-Object
(including -ExpandProperty
parameter), and Where-Object
to select the registry values that I want (prior to moving on to incorporating deletion). I am now at a point where I am entirely clueless and unable to figure out how to simply find registry values like \*Text\*
existing at a particular registry key and delete them. Something so simple seems so difficult! I do not know how to work with data in arrays!
Get-Item -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs
Every registry value is being listed as part of an array under $\_.Property
for registry key HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs
. I was expecting results where I could do something roughly like $\_.RegistryValue -Like '\*Text\*' | Remove-Item
to delete the registry values matching \*Text\*
.
For clarification to ensure correct technical usage of certain vocabulary, a "registry key" appears as a folder in regedit.exe.
A "registry value" is an entry of any type such as REG\_SZ
, REG\_DWORD
, REG\_MZ
, and so on. A registry value contains "registry data", and depending on the registry type, it may be a string, an 32-bit value (sometimes expressed as either 1, 0, or 0x000000, 0x000001).
We often refer to "registry values" as 'registry keys' (which is incorrect technical usage) and the "registry data" as 'registry values' (also incorrect technical usage), and "registry keys" as folders/location/"this place in the registry".
Update, based on your own simplification:
# The target registry key's full path.
$keyPath = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs'
# Pass the value name pattern to Remove-ItemProperty's -Name parameter.
# Remove `-WhatIf` if the preview suggests that the operation works as intended.
Remove-ItemProperty -Path $keyPath -Name *Text* -WhatIf
Pitfall: If you want to use a wildcard expression such as *Text*
with -Name
, you must combine it with -Path
rather than -LiteralPath
, even if the key path is itself not a wildcard; with -LiteralPath
, -Name
too is then taken literally (verbatim).
If you do need to use -LiteralPath
(e.g., if the literal path contains a *
character, such as in HKEY_CLASSES_ROOT\*
):
# The target registry key's full path.
$keyPath = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs'
# Get an array of all value names that match the name wildcard pattern.
$valueNames = (Get-Item -LiteralPath $keyPath).Property -like '*Text*'
# Pass the array of value names to Remove-ItemProperty's -Name parameter.
# Remove `-WhatIf` if the preview suggests that the operation works as intended.
if ($valueNames) {
Remove-ItemProperty -LiteralPath $keyPath -Name $valueNames -WhatIf
}
Note the use of Get-Item
rather than Get-ItemProperty
.[1]
Get-Item
returns an object representing the entire key that is of type [Microsoft.Win32.RegistryKey]
, which PowerShell decorates with a .Property
note property that contains an array of all the key's value names.
Important: In the .Property
array, PowerShell translates the default value name, which is the empty string (''
) at the API level, into name '(default)'
.
Applying operator -like
to an array LHS makes it act as a filter that only returns a sub-array of matching items.
Remove-ItemProperty
's -Name
parameter directly accepts an array of property (registry value) names to remove from the target key.
Note: Due to use of the -PipelineVariable
common parameter, this solution requires PSv4+.
# The target registry key's full path.
$keyPath = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs'
$pattern = '*Text*'
# Look for $pattern in both the name and the data.
# Remove `-WhatIf` if the preview suggests that the operation works as intended.
Get-Item -LiteralPath $keyPath -PipelineVariable key |
ForEach-Object Property |
Where-Object {
$valueName = ($_, '')[$_ -eq '(default)'] # translate '(default)' to '' for API
$valueName -like $pattern -or $key.GetValue($valueName) -like $pattern
} |
Remove-ItemProperty -LiteralPath $keyPath -WhatIf
-PipelineVariable key
stores the [Microsoft.Win32.RegistryKey]
instance returned by Get-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 PowerShell adds to the output [Microsoft.Win32.RegistryKey]
instance, as discussed).
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.Whatever value names match the criteria are then piped to Remove-ItemProperty
, which implicitly binds these names to its -Name
parameter.
If you simply want to list matching values and their data, see this answer.
[1] Get-ItemProperty -Path $keyPath -Name *Text*
technically works too, but the output is a single object of type [pscustomobject]
, whose properties you have to enumerate via reflection, because the property names reflect the matching value names; while doing so via .psobject.properties
as shown in this answer works and allows you to filter by data too, the pitfall is that PowerShell's registry provider automatically adds its own properties to the collection of properties, namely PSPath
, PsParentPath
, PSChildName
, PSDrive
, PSProvider
, which means that a wildcard expression that filters by name can accidentally include them or, worse, if values by the same name happen to exist on the key (even though unlikely), the provider properties overrides them.