直接从 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"
}
}
我尝试卸载一个 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"
}
}