问题描述
我尝试卸载 msi 文件,但是当我通过数组尝试此操作时出现错误(找不到安装包)
当我做同样的事情但不在数组中时 - 它有效
for ($i=0; $i -lt $msiArrayClean.length; $i++){
Write-Host $msiArrayClean[$i]
& msiexec.exe /x $msiArrayClean[$i]
}
我是如何来到 $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,因为它的性能是问题所在。我更喜欢通过注册表来做,它对我有用很多次。注释中的代码说明。
CreateMatrix = Matrix
编辑:在 Nehat 的评论之后添加了带有 msi 路径的解决方案,因为这对我有用(我试图最小化代码:))
$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
}
}
}
}
,
前面的一些警告:
-
Format-*
cmdlet 输出对象,其唯一目的是向 PowerShell 的输出格式系统提供格式说明 - 参见 this answer。简而言之:只使用Format-*
cmdlet 来格式化数据用于显示,永远不要用于后续的程序化处理。 -
CIM cmdlet(例如
Get-CimInstance
)取代了 PowerShell v3(2012 年 9 月发布)中的 WMI cmdlet(例如Get-WmiObject
)。因此,应该避免使用 WMI cmdlet,尤其是因为 PowerShell (Core)(版本 6 及更高版本),未来所有的努力都将进行,甚至没有了。有关详细信息,请参阅 this answer。 -
不鼓励使用
Win32_Product
WMI 类,出于性能原因和潜在的副作用 - 请参阅 this Microsoft article。-
另一种选择 - 仅在 Windows PowerShell 中可用(不在 PowerShell (Core) 7+ 中) - 是使用以下命令来获取卸载命令行和通过
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"
}
}