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
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:
$uninstallString = $_.GetValue('UninstallString')
$isExeOnly = Test-Path -ErrorAction Ignore -LiteralPath $uninstallString
if (-not $isExeOnly) { $uninstallString += ' /quiet /norestart' }
/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:
cmd.exe
or no-shell invocations, whereas PowerShell's different syntax rules could break the invocation; see this answer for more information.