如何在PowerShell中向运行的监控进程发送's'按键并获取返回值
解决方案:替换模拟按键为可靠的进程同步方式
直接模拟's'键的方案不可行,因为监控脚本里的$Host.UI.RawUI.ReadKey()读取的是当前PowerShell宿主的控制台输入,而非进程的标准输入流——即使你往监控进程的标准输入写入字符,它也接收不到。跨进程模拟系统级按键(比如SendKeys)还依赖窗口焦点,稳定性差。
推荐两种更可靠的实现方式:
方式一:让监控脚本直接检测目标进程状态
利用第一个脚本启动的第三个进程ID,让监控脚本循环检查该进程是否存活,一旦进程退出就自动计算结果并返回。
修改第一个脚本
# some code $psi = New-Object "Diagnostics.ProcessStartInfo" $psi.FileName = "artillery" $arguments = "my fancy args" $psi.Arguments = $arguments # 启用标准输出/错误重定向(如果需要) $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.UseShellExecute = $false $proc = [Diagnostics.Process]::Start($psi) # 将目标进程ID作为参数传给监控脚本 $psi2 = New-Object "Diagnostics.ProcessStartInfo" $psi2.FileName = "powershell.exe" $psi2.Arguments = "-File artillery_metric_getter.ps1 -TargetProcessId $($proc.Id)" $psi2.RedirectStandardOutput = $true $psi2.UseShellExecute = $false $proc2 = [Diagnostics.Process]::Start($psi2) if ($waitForExit) { $proc.WaitForExit() $stdout = $proc.StandardOutput.ReadToEnd() $stderr = $proc.StandardError.ReadToEnd() Write-Host "stdout: $stdout" Write-Host "stderr: $stderr" } # 等待监控脚本退出并读取返回结果 $proc2.WaitForExit() $monitorOutput = $proc2.StandardOutput.ReadToEnd().Trim() $meanProcRet, $meanMemRet = $monitorOutput -split '\s+' Write-Host "监控结果:CPU均值=$meanProcRet,内存均值=$meanMemRet"
修改监控脚本
param( [Parameter(Mandatory=$true)] [int]$TargetProcessId ) while ($true) { # 这里执行你的监控逻辑(比如采集指标) # ... # 检查目标进程是否仍在运行 if (-not (Get-Process -Id $TargetProcessId -ErrorAction SilentlyContinue)) { # 执行结果计算 $meanMemRet = 123 # 替换为你的实际计算逻辑 $meanProcRet = 456 # 替换为你的实际计算逻辑 # 将结果输出到标准输出,供父进程读取 Write-Output "$meanProcRet $meanMemRet" exit } # 保留手动按's'退出的功能 if ($Host.UI.RawUI.KeyAvailable) { $key = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") if ($key.Character -eq 's') { $meanMemRet = 123 $meanProcRet = 456 Write-Output "$meanProcRet $meanMemRet" exit } } # 避免循环占用过高CPU,添加延迟 Start-Sleep -Seconds 1 }
方式二:使用命名管道实现进程间通信
通过命名管道让第一个脚本在第三个进程结束后,主动给监控脚本发送结束信号。
修改第一个脚本
# some code # 创建命名管道 $pipeName = "ArtilleryMonitor_Pipe_$(Get-Random)" $pipe = New-Object System.IO.Pipes.NamedPipeServerStream( $pipeName, [System.IO.Pipes.PipeDirection]::Out, 1, [System.IO.Pipes.PipeTransmissionMode]::Message ) # 启动监控脚本并传递管道名 $psi2 = New-Object "Diagnostics.ProcessStartInfo" $psi2.FileName = "powershell.exe" $psi2.Arguments = "-File artillery_metric_getter.ps1 -PipeName $pipeName" $psi2.RedirectStandardOutput = $true $psi2.UseShellExecute = $false $proc2 = [Diagnostics.Process]::Start($psi2) # 等待监控脚本连接管道 $pipe.WaitForConnection() # 启动第三个进程 $psi = New-Object "Diagnostics.ProcessStartInfo" $psi.FileName = "artillery" $arguments = "my fancy args" $psi.Arguments = $arguments $psi.RedirectStandardOutput = $true $psi.RedirectStandardError = $true $psi.UseShellExecute = $false $proc = [Diagnostics.Process]::Start($psi) if ($waitForExit) { $proc.WaitForExit() $stdout = $proc.StandardOutput.ReadToEnd() $stderr = $proc.StandardError.ReadToEnd() Write-Host "stdout: $stdout" Write-Host "stderr: $stderr" } # 发送结束信号给监控脚本 $writer = New-Object System.IO.StreamWriter($pipe) $writer.WriteLine("STOP") $writer.Flush() $pipe.Close() # 获取监控脚本返回结果 $proc2.WaitForExit() $monitorOutput = $proc2.StandardOutput.ReadToEnd().Trim() $meanProcRet, $meanMemRet = $monitorOutput -split '\s+' Write-Host "监控结果:CPU均值=$meanProcRet,内存均值=$meanMemRet"
修改监控脚本
param( [Parameter(Mandatory=$true)] [string]$PipeName ) $stopFlag = $false # 连接命名管道并启动后台线程监听信号 $pipe = New-Object System.IO.Pipes.NamedPipeClientStream( ".", $PipeName, [System.IO.Pipes.PipeDirection]::In ) $pipe.Connect() $reader = New-Object System.IO.StreamReader($pipe) $listenThread = [System.Threading.Thread]::new({ param($reader, $stopFlagRef) try { $msg = $reader.ReadLine() if ($msg -eq "STOP") { $stopFlagRef.Value = $true } } finally { $reader.Close() $pipe.Close() } }, $reader, [ref]$stopFlag) $listenThread.Start() while (-not $stopFlag) { # 这里执行你的监控逻辑 # ... # 保留手动退出功能 if ($Host.UI.RawUI.KeyAvailable) { $key = $Host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown") if ($key.Character -eq 's') { $stopFlag = $true } } Start-Sleep -Seconds 1 } # 计算并输出结果 $meanMemRet = 123 $meanProcRet = 456 Write-Output "$meanProcRet $meanMemRet" exit
方案选择
- 方式一实现最简单,无需额外IPC机制,依赖进程ID检测状态,逻辑清晰可靠,优先推荐。
- 方式二更灵活,适用于需要复杂进程间通信的场景,但代码相对繁琐。
内容的提问来源于stack exchange,提问作者The Trainer
相关产品推荐
相关产品推荐

