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

如何用subprocess.Popen通过PowerShell(SSH)实现跨PC测试运行时管控?

问题分析与解决方案

问题根源

当你使用["start", powershell_path, ...]搭配shell=True启动PowerShell时,实际启动的是cmd.exe。cmd执行start命令后会创建一个完全独立的PowerShell进程,这个新进程的输入输出直接绑定系统控制台(conhost.exe),和你创建的process对象的管道没有任何关联——你往process.stdin写入的内容只会传给cmd进程,根本无法到达新的PowerShell窗口。同时,stdout.readline()冻结是因为cmd进程本身没有输出,父进程持续等待读取导致阻塞。


解决方案1:直接创建独立PowerShell窗口(无需start命令)

通过subprocess.Popen直接启动PowerShell,利用Windows API的CREATE_NEW_CONSOLE标记创建独立窗口,同时保留管道连接以管控输入输出。

from pathlib import Path
import subprocess

# Windows API常量:创建新的控制台窗口
CREATE_NEW_CONSOLE = 0x00000010

DATA_BANK_SCRIPTS = {
    "HelloUser": {
        "Please write your name:": "Stack",
        "Please write your age:": "99"
    },
    "AddTwoNumbers": {
        "Please write first number:": "10",
        "Please write second number:": "20",
    }
}

powershell_path = r"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe"

# 启动独立窗口的PowerShell,同时保留管道通信
process = subprocess.Popen(
    [powershell_path, "-ExecutionPolicy", "Bypass", "-NoExit"],
    stdin=subprocess.PIPE,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    creationflags=CREATE_NEW_CONSOLE,
    text=True  # 启用文本模式,自动处理编码,无需手动转换
)

for key, value_dict in DATA_BANK_SCRIPTS.items():
    script_path = Path(__file__).resolve().parent.joinpath(f"Tests/{key}.py")
    # 发送运行测试脚本的命令
    process.stdin.write(f"python {script_path}\n")
    process.stdin.flush()

    current_output = process.stdout.readline()
    while current_output:
        current_line = current_output.strip()
        print(current_line)

        if current_line in value_dict:
            # 自动写入预设输入数据
            process.stdin.write(f"{value_dict[current_line]}\n")
            process.stdin.flush()
        current_output = process.stdout.readline()

关键说明

  • CREATE_NEW_CONSOLE标记保证PowerShell在独立窗口启动,且父进程的管道仍能与该进程通信。
  • text=True参数简化编码处理,避免手动调用encode()/decode()。

解决方案2:命名管道实现跨进程通信(适配必须用start的场景)

如果因特殊需求必须使用start命令,可通过Windows命名管道实现父进程与独立PowerShell窗口的输入输出交互。

from pathlib import Path
import subprocess
import win32pipe
import win32file

# 创建命名管道
pipe_name = r"\\.\pipe\TestControlPipe"
pipe = win32pipe.CreateNamedPipe(
    pipe_name,
    win32pipe.PIPE_ACCESS_DUPLEX,
    win32pipe.PIPE_TYPE_MESSAGE | win32pipe.PIPE_READMODE_MESSAGE | win32pipe.PIPE_WAIT,
    1, 65536, 65536,
    0,
    None
)

# 启动PowerShell窗口并让其连接命名管道
powershell_cmd = (
    r'start powershell.exe -ExecutionPolicy Bypass -NoExit -Command '
    r'"$pipe = New-Object System.IO.Pipes.NamedPipeClientStream(\'.\', \'TestControlPipe\', [System.IO.Pipes.PipeDirection]::InOut); '
    r'$pipe.Connect(); $reader = New-Object System.IO.StreamReader($pipe); '
    r'$writer = New-Object System.IO.StreamWriter($pipe); $writer.AutoFlush = $true; '
    r'while($line = $reader.ReadLine()) { Invoke-Expression $line | Out-String | ForEach-Object { $writer.WriteLine($_) } }"'
)
subprocess.Popen(powershell_cmd, shell=True)

# 等待PowerShell连接管道
win32pipe.ConnectNamedPipe(pipe, None)

# 封装命令发送与结果读取函数
def send_and_receive(cmd):
    win32file.WriteFile(pipe, cmd.encode() + b"\n")
    result = b""
    while True:
        chunk = win32file.ReadFile(pipe, 4096)[1]
        if not chunk:
            break
        result += chunk
    return result.decode()

# 示例:运行测试脚本并处理交互
script_path = Path(__file__).resolve().parent.joinpath("Tests/HelloUser.py")
output = send_and_receive(f"python {script_path}")
print(output)
# 检测到输入提示后发送预设数据
send_and_receive("Stack")

注意事项

  • 该方案依赖pywin32库,若PC1无法联网,需提前离线安装该库。
  • 命名管道是Windows原生进程间通信机制,可实现完全独立窗口的输入输出管控。

针对PC2 Ubuntu设备的额外提示

如果最终需求是管控PC2(Ubuntu 12.04)上的测试系统,无需在PC1打开PowerShell窗口,推荐通过本地SSH连接(串口/交叉网线建立无网络连接),使用paramiko库远程执行命令并处理交互——逻辑与你第一次尝试的本地脚本类似,仅需将subprocess替换为远程SSH会话的stdin/stdout,这种方式更稳定可靠。

内容的提问来源于stack exchange,提问作者Andrew

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 18:15:40