¿Cómo puedo desinstalar una aplicación usando PowerShell?

136

¿Hay una manera simple de conectarse a la funcionalidad estándar ' Agregar o quitar programas ' usando PowerShell para desinstalar una aplicación existente ? ¿O para verificar si la aplicación está instalada?

Rob Paterson
fuente

Respuestas:

160
$app = Get-WmiObject -Class Win32_Product | Where-Object { 
    $_.Name -match "Software Name" 
}

$app.Uninstall()

Editar: Rob encontró otra forma de hacerlo con el parámetro Filtro:

$app = Get-WmiObject -Class Win32_Product `
                     -Filter "Name = 'Software Name'"
Jeff Hillman
fuente
1
Esto es más o menos, diría que puede ser mejor usar IdentifyingNumber en lugar del nombre, por si acaso.
Tinas
66
Después de un poco de investigación, también puede usar la cláusula -filter de Get-WmiObject: $ app = Get-WmiObject -Class Win32_Product -filter "select * from Win32_Product WHERE name = 'Software Name'"
Rob Paterson
8
Tenga en cuenta que mirar WMI solo funcionará para productos que se instalaron a través de un MSI.
EBGreen
77
Esta clase WMI toma FOREVER para enumerar. Le sugiero a Jeff que actualice su código para incluir la sugerencia de Rob.
halr9000
44
(gwmi Win32_Product | ? Name -eq "Software").uninstall() Un pequeño código de golf.
roundar
51

EDITAR: a lo largo de los años, esta respuesta ha recibido bastantes votos a favor. Me gustaría agregar algunos comentarios. Desde entonces no he usado PowerShell, pero recuerdo haber observado algunos problemas:

  1. Si hay más coincidencias que 1 para el siguiente script, no funciona y debe agregar el filtro de PowerShell que limita los resultados a 1. Creo que es -First 1 pero no estoy seguro. Siéntase libre de editar.
  2. Si MSI no instala la aplicación, no funciona. La razón por la que se escribió a continuación es porque modifica el MSI para desinstalar sin intervención, lo que no siempre es el caso predeterminado cuando se usa la cadena de desinstalación nativa.

Usar el objeto WMI lleva una eternidad. Esto es muy rápido si solo conoce el nombre del programa que desea desinstalar.

$uninstall32 = gci "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match "SOFTWARE NAME" } | select UninstallString
$uninstall64 = gci "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match "SOFTWARE NAME" } | select UninstallString

if ($uninstall64) {
$uninstall64 = $uninstall64.UninstallString -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstall64 = $uninstall64.Trim()
Write "Uninstalling..."
start-process "msiexec.exe" -arg "/X $uninstall64 /qb" -Wait}
if ($uninstall32) {
$uninstall32 = $uninstall32.UninstallString -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstall32 = $uninstall32.Trim()
Write "Uninstalling..."
start-process "msiexec.exe" -arg "/X $uninstall32 /qb" -Wait}
nickdnk
fuente
1
¡Gracias por esto! Estoy tratando de usar esto -like "appNam*"ya que la versión está en el nombre y cambia, pero no parece encontrar el programa. ¿Algunas ideas?
NSouth
1
Busque la función similar para powershell, descubra qué filtro usar y cómo hacer que coincida con su cadena correctamente. Simplemente use el shell para probar, y una vez que lo haga bien, reemplace el -match :)
nickdnk
2
Esto es oro Personalmente, elimino la 'b' de '/ qb' para que no tenga que ver ningún cuadro de diálogo.
WhiteHotLoveTiger
Mucho más rápido :-)
Oscar Foley
3
Convertí esto en un script .ps1 con prompt y una información de "lo que estoy a punto de desinstalar". gist.github.com/chrisfcarroll/e38b9ffcc52fa9d4eb9ab73b13915f5a
Chris F Carroll
34

Para arreglar el segundo método en la publicación de Jeff Hillman, puedes hacer lo siguiente:

$app = Get-WmiObject 
            -Query "SELECT * FROM Win32_Product WHERE Name = 'Software Name'"

O

$app = Get-WmiObject -Class Win32_Product `
                     -Filter "Name = 'Software Name'"
Robert Wagner
fuente
Solo un aviso ... descubrí que usar la opción "-Query" en lugar de la opción "-Filter" no devolvía un WmiObject, por lo que no tenía un método de "desinstalación".
Doug J. Huras
7

Descubrí que la clase Win32_Product no se recomienda porque desencadena reparaciones y no está optimizada para consultas. Fuente

Encontré esta publicación de Sitaram Pamarthi con un script para desinstalar si conoces el guid de la aplicación. También proporciona otro script para buscar aplicaciones realmente rápido aquí .

Use así:. \ Uninstall.ps1 -GUID {C9E7751E-88ED-36CF-B610-71A1D262E906}

[cmdletbinding()]            

param (            

 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
 [string]$ComputerName = $env:computername,
 [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,Mandatory=$true)]
 [string]$AppGUID
)            

 try {
  $returnval = ([WMICLASS]"\\$computerName\ROOT\CIMV2:win32_process").Create("msiexec `/x$AppGUID `/norestart `/qn")
 } catch {
  write-error "Failed to trigger the uninstallation. Review the error message"
  $_
  exit
 }
 switch ($($returnval.returnvalue)){
  0 { "Uninstallation command triggered successfully" }
  2 { "You don't have sufficient permissions to trigger the command on $Computer" }
  3 { "You don't have sufficient permissions to trigger the command on $Computer" }
  8 { "An unknown error has occurred" }
  9 { "Path Not Found" }
  9 { "Invalid Parameter"}
 }
Ricardo
fuente
7

Para agregar un poco a esta publicación, necesitaba poder eliminar el software de varios servidores. Usé la respuesta de Jeff para llevarme a esto:

Primero obtuve una lista de servidores, utilicé una consulta AD , pero puede proporcionar la matriz de nombres de computadora como desee:

$computers = @("computer1", "computer2", "computer3")

Luego los recorrí, agregando el parámetro -computer a la consulta gwmi:

foreach($server in $computers){
    $app = Get-WmiObject -Class Win32_Product -computer $server | Where-Object {
        $_.IdentifyingNumber -match "5A5F312145AE-0252130-432C34-9D89-1"
    }
    $app.Uninstall()
}

Utilicé la propiedad IdentifyingNumber para hacer coincidir en lugar de nombre, solo para asegurarme de que estaba desinstalando la aplicación correcta.

David Stetler
fuente
Simplemente encantadora esta solución
Raffaeu
6
function Uninstall-App {
    Write-Output "Uninstalling $($args[0])"
    foreach($obj in Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") {
        $dname = $obj.GetValue("DisplayName")
        if ($dname -contains $args[0]) {
            $uninstString = $obj.GetValue("UninstallString")
            foreach ($line in $uninstString) {
                $found = $line -match '(\{.+\}).*'
                If ($found) {
                    $appid = $matches[1]
                    Write-Output $appid
                    start-process "msiexec.exe" -arg "/X $appid /qb" -Wait
                }
            }
        }
    }
}

Llámalo de esta manera:

Uninstall-App "Autodesk Revit DB Link 2019"
Ehsan Iran-Nejad
fuente
6

Una línea de código:

get-package *notepad* |% { & $_.Meta.Attributes["UninstallString"]}
Francesco Mantovani
fuente
3

Haré mi pequeña contribución. Necesitaba eliminar una lista de paquetes de la misma computadora. Este es el guión que se me ocurrió.

$packages = @("package1", "package2", "package3")
foreach($package in $packages){
  $app = Get-WmiObject -Class Win32_Product | Where-Object {
    $_.Name -match "$package"
  }
  $app.Uninstall()
}

Espero que esto sea útil.

Tenga en cuenta que le debo a David Stetler el crédito por este guión, ya que se basa en el suyo.

Ben Key
fuente
2

Aquí está el script de PowerShell usando msiexec:

echo "Getting product code"
$ProductCode = Get-WmiObject win32_product -Filter "Name='Name of my Software in Add Remove Program Window'" | Select-Object -Expand IdentifyingNumber
echo "removing Product"
# Out-Null argument is just for keeping the power shell command window waiting for msiexec command to finish else it moves to execute the next echo command
& msiexec /x $ProductCode | Out-Null
echo "uninstallation finished"
RBT
fuente
Combiné este enfoque con los siguientes indicadores , por alguna razón, esto funciona mejor que los otros enfoques para mí.
David Rogers
1

Basado en la respuesta de Jeff Hillman:

Aquí hay una función que puede agregar profile.ps1o definir en su sesión actual de PowerShell:

# Uninstall a Windows program
function uninstall($programName)
{
    $app = Get-WmiObject -Class Win32_Product -Filter ("Name = '" + $programName + "'")
    if($app -ne $null)
    {
        $app.Uninstall()
    }
    else {
        echo ("Could not find program '" + $programName + "'")
    }
}

Digamos que desea desinstalar Notepad ++ . Simplemente escriba esto en PowerShell:

> uninstall("notepad++")

Solo tenga en cuenta que Get-WmiObjectpuede tomar algo de tiempo, ¡así que sea paciente!

Cañón Kolob
fuente
0

Utilizar:

function remove-HSsoftware{
[cmdletbinding()]
param(
[parameter(Mandatory=$true,
ValuefromPipeline = $true,
HelpMessage="IdentifyingNumber can be retrieved with `"get-wmiobject -class win32_product`"")]
[ValidatePattern('{[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}}')]
[string[]]$ids,
[parameter(Mandatory=$false,
            ValuefromPipeline=$true,
            ValueFromPipelineByPropertyName=$true,
            HelpMessage="Computer name or IP adress to query via WMI")]
[Alias('hostname,CN,computername')]
[string[]]$computers
)
begin {}
process{
    if($computers -eq $null){
    $computers = Get-ADComputer -Filter * | Select dnshostname |%{$_.dnshostname}
    }
    foreach($computer in $computers){
        foreach($id in $ids){
            write-host "Trying to uninstall sofware with ID ", "$id", "from computer ", "$computer"
            $app = Get-WmiObject -class Win32_Product -Computername "$computer" -Filter "IdentifyingNumber = '$id'"
            $app | Remove-WmiObject

        }
    }
}
end{}}
 remove-hssoftware -ids "{8C299CF3-E529-414E-AKD8-68C23BA4CBE8}","{5A9C53A5-FF48-497D-AB86-1F6418B569B9}","{62092246-CFA2-4452-BEDB-62AC4BCE6C26}"

No está completamente probado, pero se ejecutó con PowerShell 4.

Ejecuté el archivo PS1 como se ve aquí. Permitiéndole recuperar todos los sistemas del AD e intentando desinstalar múltiples aplicaciones en todos los sistemas.

He utilizado el número de identificación para buscar la causa del software de la entrada de David Stetlers.

No probado:

  1. No agrega identificadores a la llamada de la función en la secuencia de comandos, sino que comienza la secuencia de comandos con ID de parámetro
  2. Llamar al script con más de 1 nombre de computadora no se recupera automáticamente de la función
  3. Recuperando datos de la tubería
  4. Usando direcciones IP para conectarse al sistema

Lo que no hace:

  1. No proporciona ninguna información si el software se encontró realmente en un sistema determinado.
  2. No proporciona ninguna información sobre el fracaso o el éxito de la desinstalación.

No pude usar desinstalar (). Al intentar obtener un error, me dice que no es posible llamar a un método para una expresión que tiene un valor de NULL. En su lugar, utilicé Remove-WmiObject, que parece lograr lo mismo.

PRECAUCIÓN : Sin un nombre de computadora dado, elimina el software de TODOS los sistemas en el Directorio Activo.

user3410872
fuente
0

Para la mayoría de mis programas, los scripts en esta publicación hicieron el trabajo. Pero tuve que enfrentar un programa heredado que no pude eliminar usando msiexec.exe o la clase Win32_Product. (por alguna razón obtuve la salida 0 pero el programa todavía estaba allí)

Mi solución fue usar la clase Win32_Process:

con la ayuda de nickdnk, este comando es obtener la ruta de desinstalación del archivo exe:

64bit:

[array]$unInstallPathReg= gci "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match $programName } | select UninstallString

32bit:

 [array]$unInstallPathReg= gci "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" | foreach { gp $_.PSPath } | ? { $_ -match $programName } | select UninstallString

Tendrá que limpiar la cadena de resultados:

$uninstallPath = $unInstallPathReg[0].UninstallString
$uninstallPath = $uninstallPath -Replace "msiexec.exe","" -Replace "/I","" -Replace "/X",""
$uninstallPath = $uninstallPath .Trim()

ahora, cuando tenga el programa relevante desinstalar la ruta del archivo exe , puede usar este comando:

$uninstallResult = (Get-WMIObject -List -Verbose | Where-Object {$_.Name -eq "Win32_Process"}).InvokeMethod("Create","$unInstallPath")

$ uninstallResult: tendrá el código de salida. 0 es el éxito

los comandos anteriores también pueden ejecutarse de forma remota: lo hice usando el comando invoke pero creo que agregar el argumento -computername puede funcionar

dsaydon
fuente