Como posso desinstalar um aplicativo usando o PowerShell?

136

Existe uma maneira simples de conectar-se à funcionalidade padrão ' Adicionar ou remover programas ' usando o PowerShell para desinstalar um aplicativo existente ? Ou para verificar se o aplicativo está instalado?

Rob Paterson
fonte

Respostas:

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

$app.Uninstall()

Edit: Rob encontrou outra maneira de fazer isso com o parâmetro Filter:

$app = Get-WmiObject -Class Win32_Product `
                     -Filter "Name = 'Software Name'"
Jeff Hillman
fonte
1
É bem isso, eu diria que pode ser melhor usar o IdentifyingNumber do que o nome, apenas por precaução.
Tubs
6
Após um pouco de pesquisa, você também pode usar a cláusula -filter de Get-WmiObject: $ app = Get-WmiObject -Class Win32_Product -filter "select * from Win32_Product WHERE nome = 'Nome do software'"
Rob Paterson,
8
Observe que observar o WMI funcionará apenas para produtos que foram instalados por meio de um MSI.
EBGreen
7
Essa classe WMI leva FOREVER para enumerar. Sugiro a Jeff que você atualize seu código para incluir a dica de Rob.
precisa saber é o seguinte
4
(gwmi Win32_Product | ? Name -eq "Software").uninstall() Um pequeno código de golfe.
roundar 18/01/19
51

EDIT: Ao longo dos anos, esta resposta recebeu alguns votos positivos. Eu gostaria de adicionar alguns comentários. Não usei o PowerShell desde então, mas lembro-me de observar alguns problemas:

  1. Se houver mais correspondências que 1 no script abaixo, ele não funcionará e você deverá acrescentar o filtro do PowerShell que limita os resultados a 1. Acredito que -First 1sim , mas não tenho certeza. Sinta-se livre para editar.
  2. Se o aplicativo não estiver instalado pelo MSI, ele não funcionará. O motivo pelo qual foi escrito como abaixo é porque ele modifica o MSI para desinstalar sem intervenção, o que nem sempre é o caso padrão ao usar a cadeia de desinstalação nativa.

O uso do objeto WMI leva uma eternidade. Isso é muito rápido se você apenas souber o nome do programa que deseja 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
fonte
1
Obrigado por isso! Estou tentando usar isso -like "appNam*"porque a versão está no nome e muda, mas parece não encontrar o programa. Alguma ideia?
NSouth
1
Procure a função -like no PowerShell, descubra qual filtro usar e como fazê-lo corresponder à sua string corretamente. Basta usar o shell para teste, e uma vez que você começa a direita substituir o -match :)
nickdnk
2
Isto é ouro. Pessoalmente, removo o 'b' do '/ qb' para que você não precise ver nenhuma caixa de diálogo.
WhiteHotLoveTiger
Muito muito mais rápido :-)
Oscar Foley
3
Transformei isso em um script .ps1 com prompt e uma informação "o que estou prestes a desinstalar". gist.github.com/chrisfcarroll/e38b9ffcc52fa9d4eb9ab73b13915f5a
Chris F Carroll
34

Para corrigir o segundo método na postagem de Jeff Hillman, você pode:

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

Ou

$app = Get-WmiObject -Class Win32_Product `
                     -Filter "Name = 'Software Name'"
Robert Wagner
fonte
Apenas um aviso ... descobri que o uso da opção "-Query" em vez da opção "-Filter" não retornou um WmiObject, portanto, não havia um método de "desinstalação".
Doug J. Huras
7

Descobri que a classe Win32_Product não é recomendada porque aciona reparos e não é otimizada para consultas. Fonte

Encontrei este post do Sitaram Pamarthi com um script para desinstalar, se você conhece o aplicativo guid. Ele também fornece outro script para procurar aplicativos muito rápido aqui .

Use desta forma:. \ 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
fonte
7

Para adicionar um pouco a este post, eu precisava poder remover o software de vários servidores. Eu usei a resposta de Jeff para me levar a isso:

Primeiro, recebi uma lista de servidores, usei uma consulta do AD , mas você pode fornecer a matriz de nomes de computadores da maneira que desejar:

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

Então eu passei por eles, adicionando o parâmetro -computer à 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()
}

Usei a propriedade IdentifyingNumber para comparar em vez do nome, apenas para ter certeza de que estava desinstalando o aplicativo correto.

David Stetler
fonte
Simplesmente adorável esta solução
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
                }
            }
        }
    }
}

Chame assim:

Uninstall-App "Autodesk Revit DB Link 2019"
Ehsan Irã-Nejad
fonte
6

Uma linha de código:

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

Eu darei minha própria pequena contribuição. Eu precisava remover uma lista de pacotes do mesmo computador. Este é o script que eu criei.

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

Espero que isso seja útil.

Note que eu devo a David Stetler o crédito por esse script, pois ele é baseado no dele.

Ben Key
fonte
2

Aqui está o script do PowerShell usando o 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
fonte
Combinei essa abordagem com os seguintes sinalizadores , por algum motivo isso funciona melhor do que as outras abordagens para mim.
David Rogers
1

Com base na resposta de Jeff Hillman:

Aqui está uma função que você pode adicionar profile.ps1ou definir na sessão atual do 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 você quisesse desinstalar o Notepad ++ . Basta digitar isso no PowerShell:

> uninstall("notepad++")

Esteja ciente de que Get-WmiObjectpode levar algum tempo; portanto, seja paciente!

Kolob Canyon
fonte
0

Usar:

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

Não foi totalmente testado, mas foi executado no PowerShell 4.

Eu executei o arquivo PS1 como é visto aqui. Permitindo que ele recupere todos os sistemas do AD e tentando desinstalar vários aplicativos em todos os sistemas.

Eu usei o IdentifyingNumber para procurar a causa do software da entrada de David Stetlers.

Não testado:

  1. Não adicionando IDs à ​​chamada da função no script, iniciando o script com IDs de parâmetro
  2. Chamar o script com mais de um nome de computador não recuperado automaticamente da função
  3. Recuperando dados do pipe
  4. Usando endereços IP para conectar-se ao sistema

O que não faz:

  1. Não fornece nenhuma informação se o software foi realmente encontrado em qualquer sistema.
  2. Ele não fornece nenhuma informação sobre falha ou sucesso da desinstalação.

Não consegui usar o uninstall (). Tentando obter um erro informando que não é possível chamar um método para uma expressão que tenha um valor NULL. Em vez disso, usei o Remove-WmiObject, que parece realizar o mesmo.

CUIDADO : Sem o nome do computador, ele remove o software de TODOS os sistemas do Active Directory.

user3410872
fonte
0

Para a maioria dos meus programas, os scripts deste Post fizeram o trabalho. Mas tive que enfrentar um programa herdado que não consegui remover usando a classe msiexec.exe ou Win32_Product. (por algum motivo, consegui a saída 0, mas o programa ainda estava lá)

Minha solução foi usar a classe Win32_Process:

com a ajuda do nickdnk, este comando é obter o caminho do arquivo de desinstalação exe:

64 bits:

[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

você precisará limpar a sequência de resultados:

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

Agora, quando você tiver o caminho do arquivo exe de desinstalação do programa relevante , poderá usar este comando:

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

$ uninstallResult - terá o código de saída. 0 é sucesso

os comandos acima também podem ser executados remotamente - eu fiz isso usando o comando invoke, mas acredito que adicionar o argumento -computername pode funcionar

dsaydon
fonte