如何用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
相关产品推荐
相关产品推荐

