如何向subprocess.Popen子进程发送ESC键以关闭启动弹窗?
我用的某个程序有个已知Bug:启动时会尝试打开不存在的XML文件,触发警告弹窗,按ESC键就能关掉。用subprocess.run()的代码能正常解决这个问题:
ESCAPE_KEY = b'0x1b' subprocess.run(PROGRAM_EXE, stdout=subprocess.PIPE, input=ESCAPE_KEY)
但我想改用Popen来方便后续终止进程,下面的代码却没效果:
ESCAPE_KEY = b'0x1b' p = subprocess.Popen(PROGRAM_EXE, stdout=subprocess.PIPE, stdin=subprocess.PIPE) p.communicate(ESCAPE_KEY)
请问我哪里操作错了?
问题分析与解决办法
嘿,我来帮你捋清楚这里的问题,主要有两个关键错误:
1. ESC键的字节值完全写错了
你定义的ESCAPE_KEY = b'0x1b'是个大坑!这不是ESC键对应的字节,而是把字符串"0x1b"转换成了字节(也就是四个字符:'0'、'x'、'1'、'b'的ASCII码)。真正的ESC控制字符对应的是单字节的十六进制0x1b,在Python里要写成b'\x1b'。
说起来,你之前的subprocess.run()代码能生效有点奇怪——按道理这个错误的字节值也不应该触发ESC效果,可能是你实际测试时不小心写对了?不管怎样,这个字节值的问题是首要需要修正的。
2. communicate()的使用方式不符合你的需求
p.communicate(input)会做两件事:把输入发送给进程,然后阻塞等待进程退出。这就意味着,你还没来得及手动终止进程,代码就已经卡在communicate()这里等程序结束了,完全违背了你想用Popen来后续终止进程的初衷。
另外还有个时机问题:程序启动后,弹窗不会立刻出现,communicate()会在进程刚启动就发送输入,此时弹窗还没加载出来,输入自然不会被处理。
修正后的代码
下面是调整后的代码,既能自动关闭弹窗,又能保留进程句柄方便后续终止:
import subprocess import time # 修正ESC字节值 ESCAPE_KEY = b'\x1b' # 启动进程,保留stdin/stdout管道 p = subprocess.Popen(PROGRAM_EXE, stdout=subprocess.PIPE, stdin=subprocess.PIPE) # 等待1秒,确保程序启动并弹出警告弹窗(时间可以根据实际情况调整) time.sleep(1) # 发送ESC键字节并强制刷新缓冲区,确保数据被进程接收 p.stdin.write(ESCAPE_KEY) p.stdin.flush() # 后续需要终止进程时,调用下面的方法 # p.terminate() # 温和终止 # p.kill() # 强制杀死
额外提醒:如果是GUI程序的话...
如果你的程序是GUI应用(不是控制台程序),那通过标准输入发送ESC可能根本没用——因为GUI弹窗的键盘事件是由系统窗口管理器处理的,和进程的标准输入完全无关。这种情况下,你需要用专门的模拟键盘工具,比如pyautogui:
import subprocess import pyautogui import time # 启动GUI程序 p = subprocess.Popen(PROGRAM_EXE) # 等待弹窗加载完成 time.sleep(1) # 模拟按下ESC键 pyautogui.press('esc') # 后续终止进程 p.terminate()
内容的提问来源于stack exchange,提问作者moto

