直接从 msi 位置路径和 for 循环中卸载时出现问题

Problem when uninstall direct from msi location path & in for loop

我尝试卸载一个 msi 文件,但是当我通过数组尝试卸载时出现错误(找不到安装包)

当我执行相同但不在数组中时 - 它有效

for ($i=0; $i -lt $msiArrayClean.length; $i++){
  
        Write-Host $msiArrayClean[$i]
        & msiexec.exe /x $msiArrayClean[$i]

}

here the output of Write Host

我是如何来到 $msiArrayClean

 $msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*"  | Format-Table LocalPackage -AutoSize -HideTableHeaders
    $msiString = $msiCache | Out-String
    $msiArrayWithEmptyLines = $msiString -split "`n"
    $msiArray = $msiArrayWithEmptyLines.Split('', [System.StringSplitOptions]::RemoveEmptyEntries)
    $msiArrayCleanString = $msiArray | Out-String
    $msiArrayClean = $msiArrayCleanString -split "`n"

我不喜欢使用 WMI,因为它的性能是个问题。我更喜欢通过注册表来做,它对我有用很多次。代码解释在评论中。

$name = "7-zip"

#Get all items from registry
foreach ($obj in Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") {
    #Get DisplayName property of registry
    $dname = $obj.GetValue("DisplayName")
    #Search for given name
    if ($dname -like "*$name*") {
        #Get uninstall string (it gets you msiexec /I{APPID})
        $uninstString = $obj.GetValue("UninstallString")
        foreach ($line in $uninstString) {
            #Getting GUID from "{" to "}""
            $found = $line -match '(\{.+\}).*'
            if ($found) {
                #If found - get GUID
                $appid = $matches[1]
                Write-Output "About to uninstall app $appid"
                #Start uninstallation
                Start-Process "msiexec.exe" -arg "/X $appid /qb" -Wait
            }
        }
    }
}

编辑:在 Nehat 的评论之后添加了带有 msi 路径的解决方案,因为这对我有用(我试图最小化代码:))

$msiCache = get-wmiobject Win32_Product | Where-Object Name -like "*7-Zip*"  | Format-Table LocalPackage -AutoSize -HideTableHeaders

foreach ($msi in $msiCache | Out-String) {
    if ([string]::IsNullOrEmpty($msi)) {
        continue
    }
    Write-Host $msi
    Start-Process "msiexec.exe" -arg "/x $msi" -Wait
}

前面的一些注意事项:

  • Format-* cmdlet 输出对象,其唯一目的是向 PowerShell 的输出格式化系统提供 格式化指令 - 请参阅 this answer。简而言之:仅使用 Format-* cmdlet 来格式化数据 以供显示,从不用于后续 程序化处理 .

  • CIM cmdlet(例如,Get-CimInstance)取代了 PowerShell v3(已发布)中的 WMI cmdlet(例如,Get-WmiObject) 2012 年 9 月)。因此,应该避免使用 WMI cmdlet,尤其是因为 PowerShell (Core)(版本 6 及更高版本)甚至 都没有 他们了。有关详细信息,请参阅 this answer

  • 不鼓励使用 Win32_Product WMI class,这既是出于性能原因,也是由于潜在的不良副作用 -参见 this Microsoft article

    • 替代方案 - 仅在 Windows PowerShell 中可用(不在 PowerShell(核心)7+[=51= 中可用]) - 是使用以下命令获取卸载命令行并通过 cmd /c:

      执行它们
      Get-Package -ProviderName Programs -IncludeWindowsInstaller | 
        ForEach-Object { $_.meta.attributes['UninstallString'] }
      

如果你需要坚持Win32_Product:

# Get the MSI package paths of all installed products, where defined.
$msiCache = (Get-CimInstance Win32_Product).LocalPackage -ne $null

foreach ($msiPackagePath in $msiCache) {
  if (Test-Path -LiteralPath $msiPackagePath) {
    # Note that msiexec.exe runs *asynchronously*.
    # Use Start-Process -Wait to wait for each call to complete.
    & msiexec.exe /x $msiPackagePath
  } else {
    Write-Warning "Package not found: $msiPackagePath"
  }
}