1

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".

Fourat
  • 2,366
  • 4
  • 38
  • 53
BlueToast
  • 15
  • 1
  • 5
  • take a look at `Get-Help Remove-ItemProperty` for some ideas about what the problem is and how to deal with it. [*grin*] – Lee_Dailey Apr 22 '19 at 12:00
  • Remove-ItemProperty requires a -Name to be supplied, which is what I am asking for help on. Executing "Get-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs | fl" results in a great number of rows, and what I am needing help on is how to select only the items matching '\*Text\*'. That's the very issue I'm needing help on. How can I do a "Remove-ItemProperty -Path HKLM:\blah\blah -Name -Like '\*Text\*'"? – BlueToast Apr 22 '19 at 12:17
  • if i use the `-Name` parameter like this >>> `-Name '*mfc*'` <<< i get a list of matching property items. the items in that location don't seem to have _string_ values, tho. it looks like most of them are `[int32]` values. – Lee_Dailey Apr 22 '19 at 12:26
  • See [here](https://stackoverflow.com/questions/17837876/how-to-filter-name-value-pairs-under-a-registry-key-by-name-and-value-in-powersh/46036422#46036422) – montonero Apr 22 '19 at 13:07
  • 1
    Lee_Dailey and montonero provided workable solutions (and are insightful to me on working with and manipulating arrays in general). Examples based on their input: (Removal) `Remove-ItemProperty -WhatIf -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs -Name '\*Sophos\*'` (Audit/find) `(Get-ItemProperty -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\SharedDLLs).psobject.properties | where {$\_.Name -like "\*HP\*" -or $_.value -like "\*HP\*"} | select name,value` I hope these examples help someone else, too. – BlueToast Apr 23 '19 at 08:45

2 Answers2

1

Removing registry values by value name filter only:

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.


Removing values by value name and/or data filter:

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.

    • 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.
  • 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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Along with the input from Lee_Dailey and montonero, this is also a solution, and for me an highly insightful and helpful response providing a breakdown of explanation to help me understand the 'why' aspect. I sincerely appreciate your time mklement0, Lee_Dailey, and montonero! I have experimented with the different elements to better understand and see the interactions with PowerShell of what you have provided. I'm ever so grateful! – BlueToast Apr 23 '19 at 08:56
  • Glad to hear it was helpful, @BlueToast; my pleasure. Working with the registry provider in PowerShell is definitely tricky business. – mklement0 Apr 23 '19 at 13:10
  • @BlueToast: I saw your own simplification, and I've added it to the answer, which I've also significantly restructured: Note the `-Path` / `-LiteralPath` pitfall, and the pitfall with the Registry-provider-added `PS*` properties with the `.psobject.properties` approach (see updated footnote). I've added as PSv4+ solution that avoids potential name clashes. – mklement0 Apr 23 '19 at 14:20
0

The registry provider is horrible. I just wish get-itemproperty worked like this:

# get-itemproperty2.ps1

# get-childitem skips top level key, use get-item
# can't remove default name
param([parameter(ValueFromPipeline)]$key)

process { 
  $valuenames = $key.getvaluenames() 

  if ($valuenames) { 
    $valuenames | foreach {
      $value = $_
      [pscustomobject] @{
        Path = $key -replace 'HKEY_CURRENT_USER',
          'HKCU:' -replace 'HKEY_LOCAL_MACHINE','HKLM:'
        Name = $Value
        Value = $Key.GetValue($Value)
        Type = $Key.GetValueKind($Value)
      }
    }
  } else {
    [pscustomobject] @{
      Path = $key -replace 'HKEY_CURRENT_USER',
        'HKCU:' -replace 'HKEY_LOCAL_MACHINE','HKLM:'
        Name = ''
        Value = ''
        Type = ''
    }
  }
}

Then you could just do things like:

PS C:\users\me> get-item hkcu:\key1 | get-itemproperty2

Path       Name  Value    Type
----       ----  -----    ----
HKCU:\key1 name1 value1 String
HKCU:\key1 name2 value2 String


PS C:\users\me> get-item hkcu:\key1 | get-itemproperty2 | where name -eq name1

Path       Name  Value    Type
----       ----  -----    ----
HKCU:\key1 name1 value1 String


PS C:\users\me> get-item hkcu:\key1 | get-itemproperty2 | where name -eq name1 | Remove-ItemProperty -whatif
What if: Performing the operation "Remove Property" on target "Item: HKEY_CURRENT_USER\key1 Property: name1".


PS C:\users\me> get-childitem -recurse hkcu:\key1 | get-itemproperty2

Path                      Name  Value     Type
----                      ----  -----     ----
HKCU:\key1\key2           name2 value2  String
HKCU:\key1\key2                 default String
HKCU:\key1\key2\key3      name3 value3  String
HKCU:\key1\key2\key3\key4
js2010
  • 23,033
  • 6
  • 64
  • 66