簡體   English   中英

當 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”以便優雅地停止。

這個可執行文件有兩種運行模式(兩種都試過):

  • 用某種 ncurses 接管終端(考慮到它是 Windows 它可能是別的東西)
  • 只是在終端中作為常規命令運行並等待按鍵

我嘗試使用subprocess.Popen(command_parts)生成子進程,其中command_parts是一個包含可執行文件的列表,它是各種標志。

我以多種組合將以下 arguments 添加到 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()

這些嘗試都不會導致可執行文件優雅地關閉,並且會永遠徘徊,就好像標准輸入上沒有發生任何事情一樣。

觀察:

  • 如果僅從電源 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()

我得到的 output 是:

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
  • 輸入法
  • pyautogui
  • ctypes + user32.dll

例子

PyWin32

(感謝@john-hen -> 靈感來自https://stackoverflow.com/a/8117562/858565

Package: https://pypi.org/project/pywin32/

import win32console


def make_keydown_input(c) -> None:
    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])

輸入法

Package: https://pypi.org/project/pynput/

import pynput

keyb_controller = pynput.keyboard.Controller()

keyb_controller.press('q')

pyautogui

Package: https://pypi.org/project/PyAutoGUI/

示例: https://pyautogui.readthedocs.io/en/latest/keyboard.html

這可能是最簡單的方法。

import pyautogui

pyautogui.press('q')

ctypes + user32.dll

請參考這個答案: https://stackoverflow.com/a/13615802/858565

最后你只需要打電話:

PressKey(0x51)

其中0x51q的十六進制鍵碼( https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes

觀察

請注意,所有這些方法都將在調用它的程序的上下文中應用按鍵。 我還沒有嘗試過 multiprocessing.Process,也許它會做出不同的反應。

可能要擁有一個良好的隔離上下文,必須在隔離的 python 腳本中運行特定的子進程,該腳本只是通過套接字從外部接收消息。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM