2

I am trying to make a Powershell script for uninstalling software.

Here is the code:

$software = Read-Host "Software you want to remove"
$paths = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall', 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
Get-ChildItem $paths |
    Where-Object{ $_.GetValue('DisplayName') -match "$software" } |
    ForEach-Object{
        $uninstallString = $_.GetValue('UninstallString') + ' /quiet /norestart'
        Write-Host $uninstallString
        & "C:\Windows\SYSTEM32\cmd.exe" /c $uninstallString
    }

it works good for uninstall strings like

MsiExec.exe /X{C22F57FC-4B20-3354-8626-382E3C710B38} /quiet /norestart

But if I want to uninstall something like winrar which have uninstall strings like

C:\Program Files\WinRAR\uninstall.exe

I get the following error

cmd.exe : 'C:\Program' is not recognized as an internal or external command,

any idea please how to get this script working

Regards

0

2 Answers 2

2

The problem is that you're unconditionally appending /quiet /norestart.

The solution is to test if the uninstall string as a whole refers to an executable file:

$isExeOnly = Test-Path -ErrorAction Ignore -LiteralPath $uninstallString

Note: -ErrorAction Ignore ignores illegal characters in path errors that occur when a string with embedded " chars. is passed.

Based on that, you can decide how you want to handle such executables:

  • If the whole string is just an executable path and you simply want to execute it without arguments in that case (but you still want to append the desired options if the whole string is a command line, i.e. an executable with arguments):
$uninstallString = $_.GetValue('UninstallString')
$isExeOnly = Test-Path -ErrorAction Ignore -LiteralPath $uninstallString
if (-not $isExeOnly) { $uninstallString += ' /quiet /norestart' } 
  • If you do want to unconditionally pass options /quiet /norestart - even when the whole string is just an executable path - do the following, though note that the target executable may refuse to execute the resulting command line it doesn't understand these options:
$uninstallString = $_.GetValue('UninstallString')
$isExeOnly = Test-Path -ErrorAction Ignore -LiteralPath $uninstallString
if ($isExeOnly) { $uninstallString = "`"$uninstallString`"" } 
$uninstallString += ' /quiet /norestart'

The alternative is to special-case the arguments (options) to pass based on the executable file name (Split-Path -Leaf $uninstallString), but that is obviously cumbersome and impossible to do comprehensively.

Then pass the resulting string to cmd /c for execution:

cmd /c $uninstallString

Note:

  • This is the appropriate thing to do, because such uninstall strings are written for cmd.exe or no-shell invocations, whereas PowerShell's different syntax rules could break the invocation; see this answer for more information.
Sign up to request clarification or add additional context in comments.

2 Comments

I'm reading the problem as being about the path having a space and is not escaped correctly, how would adding additional arguments resolve that issue?
@NiKiZe, the premise of the question is the desire to pass additional arguments. The problem is that uninstall strings can situationally refer either to an unquoted executable name or path only or to an entire command line (executable plus arguments, which invariably involves spaces). The presence of spaces makes the value ambiguous, and you need to test which case applies: If the entire string is an executable path only that happens to contain spaces, it needs to be wrapped in "..." in order to be able to append arguments.
1

Uninstall-package works with msi installs.

For non-msi installs:

You'd have to find out the silent uninstall options for winrar. It might be '/S'. (A quick google says it is.) (if you're lucky, there's a 'quietuninstallstring')

get-package *winrar* | % { & $_.metadata['uninstallstring'] /S } 

Sometimes you have to get rid of literal double quotes:

get-package *winrar* | % { & ($_.metadata['uninstallstring'] -replace '"') /S } 

Here's an attempt of a general answer for non-msi installs where part of the uninstallstring or quietuninstallstring may or may not be in double-quotes:

$uninstall = get-package whatever | % { $_.metadata['uninstallstring'] }
# match quoted words or words
$prog, $myargs = $uninstall | select-string '("[^"]*"|\S)+' -AllMatches | 
  % matches | % value
$prog = $prog -replace '"',$null
$myargs += '/S'  # whatever silent uninstall option
& $prog $myargs

Alternative approach running uninstallstring with "cmd /c". Plus it waits for background processes. It doesn't work with "&" instead.

$uninstallstring = '"echoargs" 1 2'
cmd /c $uninstall /s

Arg 0 is <1>
Arg 1 is <2>
Arg 2 is </s>


$uninstallstring = '"notepad" file.txt'
cmd /c $uninstall

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.