You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 14:32:07