返回

Python 读取可执行文件输出的常见问题及解决指南

windows

在 Python 中读取可执行文件输出的常见问题及其解决方案

作为经验丰富的程序员,我在使用 Python 的 subprocess 模块与可执行文件交互时遇到了无法读取可执行文件输出的问题。这个问题困扰了许多 Python 开发人员,因此我决定编写这篇文章来分享我的解决方法。

问题:无法读取可执行文件输出

在 Python 中使用 subprocess 模块与可执行文件交互时,由于以下原因,可能会遇到无法读取可执行文件输出的问题:

1. 非阻塞 I/O

subprocess.Popen() 使用非阻塞 I/O,这意味着它不会等待可执行文件完成输出后再返回。因此,在尝试读取可执行文件的输出之前,需要检查它是否已完成。

2. 缓冲区溢出

默认情况下,可执行文件的输出缓冲在 subprocess.PIPE 中。如果缓冲区已满,可执行文件将挂起,直到有可用的空间。这可能会导致读取操作挂起。

解决方法

要解决此问题,可以使用以下方法:

1. 使用循环轮询

在循环中使用 process.poll() 方法检查可执行文件是否已完成。只要可执行文件仍在运行,就继续轮询。

while process.poll() is None:
    # 检查输出

2. 设置 stdout 缓冲区大小

可以使用 stdout=subprocess.PIPE, bufsize=1 来设置 stdout 缓冲区大小为 1 字节。这将强制可执行文件在每次输出一个字节时刷新缓冲区。

3. 使用 non-blocking=True

在 subprocess.Popen() 中将 non-blocking=True 设置为 True。这将使用非阻塞 I/O,但它还要求你明确调用 process.communicate() 来读取输出。

示例代码

使用循环轮询的示例代码如下:

import subprocess
import re

EXE_PATH = 'C:/path/to/executable.exe'

def get_next_context(process):
    match = None
    out = ''
    while process.poll() is None:
        out += process.stdout.read(1)
        match = re.match(r'Host IP: (.+)\nHost User: (.+)\nPassword: ', out)
        if match is not None:
            return match.group(1), match.group(2)
    return None, None

if __name__ == '__main__':
    p = subprocess.Popen([EXE_PATH], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, stdin=subprocess.PIPE, shell=True, text=True)
    try:
        out = ''
        while p.poll() is None:
            ip, user = get_next_context(p)
            if ip is None:
                # Process is done and there are no more passwords to accept
                break
            password = input('Password for host {} and user {}: '.format(ip, user))
            p.stdin.write('{}\r\n'.format(password))
            if p.poll() is None:
                p.stdin.flush()
    finally:
        p.kill()

注意事项

对于某些可执行文件,即使使用这些方法,也可能无法读取其输出。这是因为可执行文件可能使用了自己的缓冲或 I/O 机制。在这种情况下,需要研究可执行文件的文档或与开发人员联系以获得特定解决方案。

常见问题解答

1. 为什么我无法使用 process.stdout.read() 直接读取可执行文件的输出?

process.stdout.read() 会立即尝试读取可执行文件的输出,即使可执行文件尚未完成输出。这可能会导致阻塞或读取不完整的数据。

2. 我怎样知道可执行文件是否已完成输出?

可以使用 process.poll() 方法检查可执行文件是否已完成。如果 process.poll() 返回 None,表示可执行文件仍在运行。如果返回一个整数,表示可执行文件已完成,并且整数的值是退出代码。

3. 如何设置 stdout 缓冲区大小?

可以使用 subprocess.PIPE, bufsize=1 来设置 stdout 缓冲区大小为 1 字节。这将强制可执行文件在每次输出一个字节时刷新缓冲区。

4. 如何使用 non-blocking=True?

在 subprocess.Popen() 中将 non-blocking=True 设置为 True。这将使用非阻塞 I/O,但它还要求你明确调用 process.communicate() 来读取输出。

5. 对于使用自定义缓冲或 I/O 机制的可执行文件,我该怎么办?

对于这些可执行文件,需要研究其文档或与开发人员联系以获得特定解决方案。