当 Python 中的 Windows 子进程需要一个键但根本不通过标准输入对其做出反应时,如何终止它?
How to kill a Windows subprocess in Python when it expects a key but simply doesn't react to it through stdin?
我正在尝试终止一个需要在终端中按下 'q' 键才能正常停止的子进程。
这个可执行文件有两种模式运行ning(两种都试过了):
- 可能使用某种 ncurses 接管终端(考虑到它是 Windows 它可能是其他东西)
- 只是 运行 作为常规命令在终端中等待按键
我尝试使用 subprocess.Popen(command_parts)
生成子进程,其中 command_parts
是一个包含可执行文件和各种标志的列表。
我已将以下参数以多种组合添加到 Popen 构造函数中:
- 没有特殊标志
- 和
creationflags=subprocess.DETACHED_PROCESS
- 和
stdin=PIPE
我尝试将以下字符串发送到可执行文件的标准输入:
b"q"
b"q\n"
b"q\r\n"
我已尝试通过以下方式与可执行文件通信:
subprocess_instance.communicate(input_stuff)
subprocess_instance.stdin.write(input_stuff); subprocess_instance.stdin.flush()
None 这些尝试导致可执行文件正常关闭,并且永远徘徊,就好像标准输入上什么也没发生一样。
观察:
- 只要简单地 运行从 power shell
中打开可执行文件,q 击键就可以工作
- 可执行文件必须正常关闭,否则会导致一些不良行为
- Python 使用的版本:3.8.*、3.9.*
更新:
我尝试使用等待 'q':
的示例 C 程序
#include <stdio.h>
#include <conio.h>
int main(int argc, char const *argv[]) {
printf("Hello world\n");
printf("Waiting for char ...\n");
while (1) {
unsigned int x = getch();
printf("PRESSED: %c\n", x);
if (x == 'q') {
printf("Quitting ...\r\n");
break;
};
}
printf("\n----\n");
printf("DONE\n\n");
return 0;
}
然后我尝试使用 运行 的脚本是:
import time
import subprocess as sp
import pathlib as plib
def main() -> None:
print("\nSTARTING")
print("Create subproces ...");
exe_path = plib.Path('guinea_pig_executable/bla.exe').absolute()
exe_path_str = str(exe_path)
exe = sp.Popen(
[exe_path_str],
stdin=sp.PIPE,
stdout=sp.PIPE,
stderr=sp.PIPE,
)
print("Wait ...")
time.sleep(5)
print("Try quitting ...")
try:
exe.communicate(b'q\r\n', timeout=2)
# exe.stdin.write(b'q')
# exe.stdin.flush()
except Exception as err:
print(repr(err))
print("Wait for sub proc to die ...")
try:
exe.wait(timeout=5)
except sp.TimeoutExpired as terr:
print("Timeout error", repr(terr))
exe.kill() # forcefully killing
print("\nEND\n---\n")
if __name__ == '__main__':
main()
我得到的输出是:
PS E:\workspace\try-kill-exe> python .\main.py
STARTING
Create subproces ...
Wait ...
Try quitting ...
TimeoutExpired(['E:\workspace\try-kill-exe\guinea_pig_executable\bla.exe'], 2)
Wait for sub proc to die ...
Timeout error TimeoutExpired(['E:\workspace\try-kill-exe\guinea_pig_executable\bla.exe'], 5)
END
---
这可能是什么原因造成的?这是 windows 具体的吗?是不是 Python 处理不当?我还可以尝试哪些其他事情?
当涉及到按键时,python 脚本可以通过多种方式与子进程通信。
- pywin32
- pynput
- pyautogui
- ctypes +
user32.dll
例子
PyWin32
(感谢@john-hen -> 灵感来自 )
包裹:https://pypi.org/project/pywin32/
import win32console
def make_keydown_input(c):
input_record = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
input_record.KeyDown = 1
input_record.RepeatCount = 1
input_record.Char = c
return input_record
console_handler = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)
q_keydown = make_keydown_input('q')
console_handler.WriteConsoleInput([q_keydown])
pynput
套餐:https://pypi.org/project/pynput/
import pynput
keyb_controller = pynput.keyboard.Controller()
keyb_controller.press('q')
pyautogui
包裹:https://pypi.org/project/PyAutoGUI/
示例:https://pyautogui.readthedocs.io/en/latest/keyboard.html
这可能是最简单的方法。
import pyautogui
pyautogui.press('q')
ctypes + user32.dll
请参考这个回答:
最后你只需要调用:
PressKey(0x51)
其中 0x51
是 q
根据 (https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)
的十六进制键码
观察
请注意,所有这些方法都将在调用它的程序的上下文中应用按键。我没有用 multiprocessing.Process 试过,也许它会有不同的反应。
可能要有一个良好的隔离上下文,人们必须 运行 隔离 python 脚本中的特定子进程,该脚本仅通过套接字从外部接收消息。
我正在尝试终止一个需要在终端中按下 'q' 键才能正常停止的子进程。
这个可执行文件有两种模式运行ning(两种都试过了):
- 可能使用某种 ncurses 接管终端(考虑到它是 Windows 它可能是其他东西)
- 只是 运行 作为常规命令在终端中等待按键
我尝试使用 subprocess.Popen(command_parts)
生成子进程,其中 command_parts
是一个包含可执行文件和各种标志的列表。
我已将以下参数以多种组合添加到 Popen 构造函数中:
- 没有特殊标志
- 和
creationflags=subprocess.DETACHED_PROCESS
- 和
stdin=PIPE
我尝试将以下字符串发送到可执行文件的标准输入:
b"q"
b"q\n"
b"q\r\n"
我已尝试通过以下方式与可执行文件通信:
subprocess_instance.communicate(input_stuff)
subprocess_instance.stdin.write(input_stuff); subprocess_instance.stdin.flush()
None 这些尝试导致可执行文件正常关闭,并且永远徘徊,就好像标准输入上什么也没发生一样。
观察:
- 只要简单地 运行从 power shell 中打开可执行文件,q 击键就可以工作
- 可执行文件必须正常关闭,否则会导致一些不良行为
- Python 使用的版本:3.8.*、3.9.*
更新:
我尝试使用等待 'q':
的示例 C 程序#include <stdio.h>
#include <conio.h>
int main(int argc, char const *argv[]) {
printf("Hello world\n");
printf("Waiting for char ...\n");
while (1) {
unsigned int x = getch();
printf("PRESSED: %c\n", x);
if (x == 'q') {
printf("Quitting ...\r\n");
break;
};
}
printf("\n----\n");
printf("DONE\n\n");
return 0;
}
然后我尝试使用 运行 的脚本是:
import time
import subprocess as sp
import pathlib as plib
def main() -> None:
print("\nSTARTING")
print("Create subproces ...");
exe_path = plib.Path('guinea_pig_executable/bla.exe').absolute()
exe_path_str = str(exe_path)
exe = sp.Popen(
[exe_path_str],
stdin=sp.PIPE,
stdout=sp.PIPE,
stderr=sp.PIPE,
)
print("Wait ...")
time.sleep(5)
print("Try quitting ...")
try:
exe.communicate(b'q\r\n', timeout=2)
# exe.stdin.write(b'q')
# exe.stdin.flush()
except Exception as err:
print(repr(err))
print("Wait for sub proc to die ...")
try:
exe.wait(timeout=5)
except sp.TimeoutExpired as terr:
print("Timeout error", repr(terr))
exe.kill() # forcefully killing
print("\nEND\n---\n")
if __name__ == '__main__':
main()
我得到的输出是:
PS E:\workspace\try-kill-exe> python .\main.py
STARTING
Create subproces ...
Wait ...
Try quitting ...
TimeoutExpired(['E:\workspace\try-kill-exe\guinea_pig_executable\bla.exe'], 2)
Wait for sub proc to die ...
Timeout error TimeoutExpired(['E:\workspace\try-kill-exe\guinea_pig_executable\bla.exe'], 5)
END
---
这可能是什么原因造成的?这是 windows 具体的吗?是不是 Python 处理不当?我还可以尝试哪些其他事情?
当涉及到按键时,python 脚本可以通过多种方式与子进程通信。
- pywin32
- pynput
- pyautogui
- ctypes +
user32.dll
例子
PyWin32
(感谢@john-hen -> 灵感来自 )
包裹:https://pypi.org/project/pywin32/
import win32console
def make_keydown_input(c):
input_record = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
input_record.KeyDown = 1
input_record.RepeatCount = 1
input_record.Char = c
return input_record
console_handler = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)
q_keydown = make_keydown_input('q')
console_handler.WriteConsoleInput([q_keydown])
pynput
套餐:https://pypi.org/project/pynput/
import pynput
keyb_controller = pynput.keyboard.Controller()
keyb_controller.press('q')
pyautogui
包裹:https://pypi.org/project/PyAutoGUI/
示例:https://pyautogui.readthedocs.io/en/latest/keyboard.html
这可能是最简单的方法。
import pyautogui
pyautogui.press('q')
ctypes + user32.dll
请参考这个回答:
最后你只需要调用:
PressKey(0x51)
其中 0x51
是 q
根据 (https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes)
观察
请注意,所有这些方法都将在调用它的程序的上下文中应用按键。我没有用 multiprocessing.Process 试过,也许它会有不同的反应。
可能要有一个良好的隔离上下文,人们必须 运行 隔离 python 脚本中的特定子进程,该脚本仅通过套接字从外部接收消息。