1

我正在尝试获取一组应用程序的卸载路径并卸载它们。到目前为止,我得到了卸载路径的列表。但我正在努力实际卸载这些程序。

到目前为止,我的代码是。


    $app = @("msi1", "msi2", "msi3", "msi4")
     $Regpath = @(
                    'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*'
                    'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
                )
                   
    foreach ($apps in $app){
    $UninstallPath = Get-ItemProperty $Regpath | where {$_.displayname -like "*$apps*"} | Select-Object -Property UninstallString
    
    $UninstallPath.UninstallString
    #Invoke-Expression UninstallPath.UninstallString
    #start-process "msiexec.exe" -arg "X $UnistallPath /qb" - wait
    }

这将返回以下结果:


    MsiExec.exe /X{F17025FB-0401-47C9-9E34-267FBC619AAE}
    MsiExec.exe /X{20DC0ED0-EA01-44AB-A922-BD9932AC5F2C}
    MsiExec.exe /X{29376A2B-2D9A-43DB-A28D-EF5C02722AD9}
    MsiExec.exe /X{18C9B6D0-DCDC-44D8-9294-0ED24B080F0C}

我正在努力寻找执行这些卸载路径并实际卸载 MSI。

我曾尝试使用Invoke-Expression $UninstallPath.UninstallString,但它只显示 Windows 安装程序并为我提供 msiexec 选项。

我也尝试过使用start-process "msiexec.exe" -arg "X $UnistallPath /qb" - wait ,但这给出了同样的问题。

4

2 回答 2

2

笔记:

  • 这个答案解决了所问的问题。
  • js2010 的有用答案显示了一个更方便的替代方案,可以通过PackageManagement模块Get-PackageUninstall-Packagecmdlet 避免原始问题。但是请注意,这些 cmdlet 仅支持Windows PowerShell版本中已安装的应用程序- 相比之下,从 v7.1 开始的PowerShell (Core)缺少相关的包提供程序,[1]并且(对我而言)不清楚它们是否会成为添加。

问题:

  • 存储在UninstallString/QuietUninstallString注册表值[2]cmd.exe中的卸载命令行是为无 shell / from-调用而设计的。

  • 因此,如果您将它们传递给,它们可能从 PowerShellInvoke-Expression失败,即如果它们包含在 shell / to 之外没有特殊含义的未引用字符cmd.exe但它们是PowerShell 中的元字符{,这适用}于您的情况。

解决方案

你有两个选择:

  • (a)只需将卸载字符串按原样传递给cmd /c

    • 请注意,与msiexec.exe直接从 PowerShell 调用或直接从cmd.exe调用不同,通过cmd /c导致同步执行msiexec,这是可取的。
  • (b)将卸载字符串拆分为可执行文件和参数列表,这样您就可以通过以下方式调用命令Start-Process

    • 这允许您使用-Wait开关来确保在脚本继续之前完成安装。

注意:以下命令假定卸载字符串包含在变量中$UninstallString(相当于$UninstallPath.UninstallString代码中的 ):

(a) 的实施:

# Simply pass the uninstallation string (command line) to cmd.exe
# via `cmd /c`. 
# Execution is synchronous (blocks until the command finishes).
cmd /c $UninstallString

$exitCode = $LASTEXITCODE

然后可以查询自动$LASTEXITCODE变量以获取命令行的退出代码

(b)的实施:

# Split the command line into executable and argument list.
# Account for the fact that the executable name may be double-quoted.
if ($UninstallString[0] -eq '"') {
    $unused, $exe, $argList = $UninstallString -split '"', 3
}
else {
    $exe, $argList = $UninstallString -split ' ', 2
}

# Use Start-Process with -Wait to wait for the command to finish.
# -PassThru returns an object representing the process launched,
# whose .ExitCode property can then be queried.
$ps = if ($argList) {
        Start-Process -Wait -PassThru $exe $argList
      } else {
        Start-Process -Wait -PassThru $exe 
      }
$exitCode = $ps.ExitCode

您还可以添加-NoNewWindow以防止基于控制台程序的卸载命令行在新的控制台窗口中运行,但请注意,捕获其 stdout / stderr 输出的唯一方法是使用/参数Start-Process将它们重定向到files 。-RedirectStandardOutput-RedirectStandardError


特定于版本/未来的改进:

基于-Start-Process的方法很麻烦,原因有两个:

  • 您不能传递整个命令行,而必须分别指定可执行文件和参数。

  • Windows PowerShell(其最新和最终版本是 5.1)中,您不能将字符串或数组传递给(位置隐含的)-ArgumentList参数(因此需要上述两个单独的调用)。

    • 此问题已在跨平台按需安装的PowerShell (Core)版本(版本 6 及更高版本)中得到修复。

[1] 如果您不介意额外的开销,您PackageManagement甚至可以(临时)从 PowerShell(核心)导入 Windows PowerShell 模块,使用Windows PowerShell 兼容性功能
Import-Module -UseWindowsPowerShell PackageManagement.

[2] 如您的问题所示,它们存储在HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\Uninstall(64 位应用程序)和HKEY_LOCAL_MACHINE\Wow6432Node \Software\Microsoft\Windows\CurrentVersion\Uninstall(32 位应用程序)注册表项中。

于 2021-07-14T17:35:01.650 回答
2

或者例如:

get-package *chrome* | uninstall-package
于 2021-07-14T18:09:54.917 回答