0

I have been getting the uninstall string via below script.

But I am getting an output like this.

MsiExec.exe /I{629388F2-A011-4F5C-A6BF-98A80A25317C}

My desired output:

{629388F2-A011-4F5C-A6BF-98A80A25317C}

After desired output , I will assign this value to the variable such as $uninstallString. And I will use it inside my doRemoveMSI function like below.

$paths = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
Get-ChildItem $paths |
    Where-Object{ $_.GetValue('DisplayName') -match 'APP' } | 
    ForEach-Object{ 
        $uninstallString = $_.GetValue('UninstallString')
       
        $uninstallString
    }


doRemoveMSI -msi "msiexec.exe" -arguments '/x', '$uninstallString', '/quiet', 'REBOOT=R', '/L*V "C:\msilog.log"'
mklement0
  • 382,024
  • 64
  • 607
  • 775
Arbelac
  • 1,698
  • 6
  • 37
  • 90
  • TRy following : "MsiExec.exe /I{629388F2-A011-4F5C-A6BF-98A80A25317C}" -match "\{(?[^}]+)" Write-Host $Matches["id"] – jdweng Feb 27 '23 at 16:17
  • 1
    `get-package *app* | uninstall-package` – js2010 Feb 27 '23 at 16:32
  • Invoke-Expression (get-package *$app* -ErrorAction ignore).meta.Attributes["UninstallString"] (warning, this will delete the app). – rinat gadeev Feb 28 '23 at 08:48
  • @rinatgadeev, this isn't robust, because such command lines aren't written with _PowerShell_ syntax in mind, which causes problems, notably with unquoted `{...}` arguments; see [this answer](https://stackoverflow.com/a/68382753/45375). – mklement0 Feb 28 '23 at 15:49

1 Answers1

0

As an aside:

  • If you're using Windows PowerShell and only need to remove MSI-installed applications, a much more convenient solution is to use Uninstall-Package, as js2010 points out; e.g.,
    Get-Package -ProviderName msi *App* | Uninstall-Package

  • If you're looking to execute UninstallString / QuietUninstallString command lines extracted from the registry as-is, see this answer.


Your question can be reduced to how to extract a {...} substring from a larger string, which you can do with PowerShell's regex-based -replace operator:

# -> '{629388F2-A011-4F5C-A6BF-98A80A25317C}'
'MsiExec.exe /I{629388F2-A011-4F5C-A6BF-98A80A25317C}' -replace '^.+({.+?}).*$', '$1'

Note: { and } are situationally regex metacharacters, but are treated literally here, because they aren't part of valid quantifier subexpressions (such as o{2,3}). You may choose to always escape them as \{ and \}, to be safe.

Alternatively, you could use the equally regex-based -split operator, and split the string into tokens by { and }, including those separators, with the help of look-around assertions, and extracting the 2nd token ([1]):

# -> '{629388F2-A011-4F5C-A6BF-98A80A25317C}'
('MsiExec.exe /I{629388F2-A011-4F5C-A6BF-98A80A25317C}' -split '(?={)|(?<=})')[1]

See this regex101.com page for a detailed explanation of the regex and the ability to experiment with it. Note that the look-around assertions only match character positions, not actual characters, which is what enables including { and } in the tokens returned by -split.

Or the .Split() method of the .NET [string] type, passing an array of (literal) characters to split by, using an expandable (double-quoted) string ("...") with the $(...), the subexpression operator to surround the extracted token in { and } again.

# -> '{629388F2-A011-4F5C-A6BF-98A80A25317C}'
"{$( ('MsiExec.exe /I{629388F2-A011-4F5C-A6BF-98A80A25317C}').Split([char[]] '{}')[1] )}"

Speaking of expandable strings:

The '$uninstallString' in your doRemoveMSI call,

doRemoveMSI -msi "msiexec.exe" -arguments '/x', '$uninstallString', '/quiet', 'REBOOT=R', '/L*V "C:\msilog.log"'

won't work as intended, because NO expansion (string interpolation) happens inside '...', i.e. verbatim (single-quoted) strings.

While "$uninstallString" avoids that problem, you do not even need the "..." (except in cases where you need explicit to-string conversion), and can simply pass $uninstallString as-is.


As for how you're partitioning the arguments you're passing to doRemoveMSI (consider renaming the function to conform to PowerShell's naming conventions, e.g., Uninstall-Msi):

  • As discussed in this answer to your previous question, given your desire to pass argument pairs via a single string with embedded double-quoting - e.g. '/L*V "C:\msilog.log"' - it is ultimately simpler to pass a single string overall, i.e. to declare your -arguments parameter as type [string], and then invoke the function as follows:
# Note the use of a *single* "..." string, 
# with *embeded* " chars. escaped as `"
doRemoveMSI -msi msiexec.exe -arguments "/x $uninstallString /quiet REBOOT=R /L*V `"C:\msilog.log`""
mklement0
  • 382,024
  • 64
  • 607
  • 775