如何在PowerShell中捕获RabbitMQ卸载时的分离子进程
RabbitMQ无人值守卸载的进程等待问题
问题背景
在Windows系统中使用PowerShell执行RabbitMQ无人值守卸载时,通过注册表获取卸载字符串并启动进程,但遇到以下问题:
初始执行代码:
$RABBITMQ_PRODUCT_NAME = "RabbitMQ Server" $rabbitMq = Get-ChildItem -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall ` | Get-ItemProperty ` | Where-Object { $_.DisplayName -match $RABBITMQ_PRODUCT_NAME } $rabbitUninstallProcess = Start-Process -FilePath $rabbitMq.UninstallString -ArgumentList "/S" -PassThru -ErrorAction Stop # 无法使用-Wait参数,否则会无限挂起,因此需获取进程后调用WaitForExit。 $rabbitUninstallProcess.WaitForExit()
这段代码的问题是:$rabbitUninstallProcess启动真实卸载进程后就会立即退出,导致脚本在卸载完成前就收到成功状态码。
虽然可以通过进程名Un_A结合产品名称匹配找到真实卸载进程,但存在竞态条件,无法保证找到的进程就是当前启动的卸载流程创建的:
$UNINSTALL_PROCESS_NAME = "Un_A" $process = Get-Process -Name $UNINSTALL_PROCESS_NAME ` | Where-Object { $_.Product -match $RABBITMQ_PRODUCT_NAME } $process.WaitForExit()
完整脚本(含Erlang卸载逻辑)
$RABBITMQ_PRODUCT_NAME = "RabbitMQ Server" $ERLANG_PRODUCT_NAME = "Erlang OTP" $UNINSTALL_PROCESS_NAME = "Un_A" $FAILURE = 9 $rabbitMq = Get-ChildItem -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall ` | Get-ItemProperty ` | Where-Object { $_.DisplayName -match $RABBITMQ_PRODUCT_NAME } $erlang = Get-ChildItem -Path HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall ` | Get-ItemProperty ` | Where-Object { $_.DisplayName -match $ERLANG_PRODUCT_NAME } $rabbitMQDataDir = $env:RABBITMQ_BASE if ($rabbitMq) { $installedRabbitMQVersion = $rabbitMq.DisplayVersion Write-Host "Removing $RABBITMQ_PRODUCT_NAME v$installedRabbitMQVersion" # 无法使用-Wait参数,否则会无限挂起,因此需获取进程后调用WaitForExit。 $rabbitUninstallProcess = Start-Process -FilePath $rabbitMq.UninstallString -ArgumentList "/S" -PassThru -ErrorAction Stop # 该进程仅启动真实卸载进程,因此需等待其退出后再查找卸载进程 $rabbitUninstallProcess.WaitForExit() # RabbitMQ的卸载程序(来自Nullsoft)进程名为"Un_A"。 # Product属性用于识别该卸载进程属于RabbitMQ。 # 存在竞态条件:能否在进程结束前找到它?通常可以,但仍存在风险。 $process = Get-Process -Name $UNINSTALL_PROCESS_NAME ` | Where-Object { $_.Product -match $RABBITMQ_PRODUCT_NAME } if ($process) { Write-Host "Waiting for uninstall process to finish" $process.WaitForExit() } else { # 如果未找到卸载进程,但进程实际在运行且卸载在脚本失败后完成,再次运行脚本可继续处理 Write-Host "Didn't find an uninstall process. Going to sleep then check if the uninstall happened. If RabbitMQ is still installed, it will fail after the re-check." Start-Sleep -Seconds 60 } $rabbitMqStillThere = Get-ChildItem HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall ` | Get-ItemProperty ` | Where-Object { $_.DisplayName -match $RABBITMQ_PRODUCT_NAME } if ($rabbitMqStillThere) { Write-Host "Unable to uninstall RabbitMQ. You may have to uninstall manually." exit $FAILURE } else { Write-Host "$($rabbitMq.DisplayName) has been uninstalled." } } else { Write-Host "$RABBITMQ_PRODUCT_NAME not installed moving on." } # 需要清理RabbitMQ数据目录。 if([string]::IsNullOrWhiteSpace($rabbitMQDataDir)){ # 如果未设置环境变量,使用默认路径。 $rabbitMQDataDir = "C:\ProgramData\RabbitMQ" } if (Test-Path -Path $rabbitMQDataDir -PathType Container) { Write-Host "Removing RabbitMQ Data Dir $rabbitMQDataDir" Remove-Item -Path $rabbitMQDataDir -Recurse -Force -ErrorAction Stop } # 现在卸载Erlang。 if ($erlang) { $installedErlangVersion = $erlang.DisplayVersion Write-Host "Removing $ERLANG_PRODUCT_NAME v$installedErlangVersion" # Erlang卸载可正常使用-Wait参数 $erlangUninstallProcess = Start-Process -FilePath $erlang.UninstallString -ArgumentList "/S" -PassThru -Wait -ErrorAction Stop if ($erlangUninstallProcess.ExitCode -ne 0) { Write-Host "Error Removing Erlang " exit $FAILURE } else { Write-Host "$($erlang.DisplayName) has been uninstalled." } } else { Write-Host "$ERLANG_PRODUCT_NAME not installed moving on" }
预期与实际情况
预期
- 直接通过RabbitMQ卸载字符串启动的进程即为真实卸载进程,或能获取其句柄
- 可以使用
-Wait参数等待该进程完成,以确认卸载成功或失败
实际
- 使用
Start-Process -Wait会无限挂起 - 使用
Start-Process -PassThru获取进程句柄后调用WaitForExit(),该进程会在启动真实卸载进程后立即退出并返回成功状态 - 无法确定性地找到由
Start-Process启动的子进程/后代进程
内容的提问来源于stack exchange,提问作者Eric-R
相关产品推荐
相关产品推荐

