检测 child 进程何时等待标准输入

Detecting when a child process is waiting for stdin

我正在制作一个能够 运行 任何可执行文件的终端程序(请忽略安全问题)。我需要检测 child 进程何时等待用户输入(来自 stdin)。我使用以下方式启动 child 进程:

process = subprocess.Popen(command, close_fds=False, shell=True, **file_descriptors)

我可以想到两种方法来检测 child 进程是否正在等待 stdin:

win32event.WaitForInputIdle(proc._handle, 100)

但是我得到了这个错误:

(1471, 'WaitForInputIdle', 'Unable to finish the requested operation because the specified process is not a GUI process.')

也在 windows api 文档 here 中说:“WaitForInputIdle 只等待一次进程变为空闲;随后的 WaitForInputIdle 调用 return立即,无论进程是空闲还是忙碌。”。我认为这意味着我不能多次使用该功能来解决我的问题

编辑: 这只需要在 Windows 上工作,但稍后我可能会尝试使我的程序也可以用 Linux 计算。此外,我正在为 stdin/stdout/stderr.

使用管道

为什么我需要知道 child 是否正在等待标准输入:

目前,当用户按下回车键时,我将他们到目前为止写入的所有数据发送到 stdin 并禁止用户更改它。问题是当 child 进程为 sleeping/calculating 并且用户写入一些输入并希望在进程再次从 stdin 读取之前更改它。

基本上让我们看这个程序:

sleep(10)
input("Enter value:")

假设我输入了“abc\n”。使用 cmd 时,如果 child 仍在休眠,它将允许我按退格键并删除输入。目前我的程序会在检测到“\n”并将其发送到 stdin.

时将所有文本标记为只读

我不确定这是一个好的解决方案,但如果有兴趣,您可以试一试。我只是假设我们在给定 2 个输入 dataTIMEOUT.

的情况下为其 output 执行子进程
process = subprocess.Popen(command, close_fds=False, shell=True, **file_descriptors)

try:
    output, _ = process.communicate(data, TIMEOUT)
except subprocess.TimeoutExpired:
    print("Timeout expires while waiting for a child process.")
    # Do whatever you want here

    return None

cmd_output = output.decode()

您可以找到更多 TimeoutExpired 示例 here

class STDINHandle:
    def __init__(self, read_handle, write_handle):
        self.handled_write = False
        self.working = Lock()
        self.write_handle = write_handle
        self.read_handle = read_handle

    def check_child_reading(self):
        with self.working:
            # Reset the flag
            self.handled_write = True
            # Write a character that cmd will ignore
            self.write_handle.write("\r")
            thread = Thread(target=self.try_read)
            thread.start()
            sleep(0.1)
            # We need to stop the other thread by giving it data to read
            if self.handled_write:
                # Writing only 1 "\r" fails for some reason.
                # For good measure we write 10 "\r"s
                self.write_handle.write("\r"*10)
                return True
            return False

    def try_read(self):
        data = self.read_handle.read(1)
        self.handled_write = False

    def write(self, text):
        self.write_handle.write(text)

我做了一些测试,我认为 cmd 会忽略 "\r" 个字符。我找不到 cmd 将其解释为实际字符的情况(就像我做 "\b" 时发生的情况)。发送一个 "\r" 字符并测试它是否留在管道中。如果它确实留在管道中,则意味着 child 尚未处理它。如果我们无法从管道中读取它,则意味着 child 已经处理了它。但是我们有一个问题——如果我们不能从 stdin 读取,我们需要停止读取,否则它会干扰下一次写入 stdin。为此,我们向管道写入更多 "\r"s。

注意:我可能需要更改 sleep(0.1) 行的时间。