PYR-202410 2024-10-27 11:32 采纳率: 82.5%
浏览 19

使用select辅助读取subprocess.Popen进程输出时发生错误

使用select辅助读取subprocess.Popen进程输出时发生错误:

    reads, _, _ = select.select([p.stdout, p.stderr], [], [])
OSError: [WinError 10093] 应用程序没有调用 WSAStartup,或者 WSAStartup 失败。

源代码:

p = subprocess.Popen(pyinstallerPopenCommand,
                             stdout = subprocess.PIPE,
                             stderr = subprocess.PIPE,
                             cwd = popenCwd,
                             shell = True
                             )
        reads, _, _ = select.select([p.stdout, p.stderr], [], []) # 此处报错
        for r in reads:
            line = r.readline()
            line = line.strip()
            if line:
                self.returnInfo(line.decode("utf-8"))
            line = r.readline()
            line = line.strip()
            if line:
                self.returnInfo(line.decode("utf-8"))
  • 写回答

1条回答 默认 最新

  • 越重天 新星创作者: Java技术领域 2024-10-27 11:47
    关注

    本答案参考 多次询问 ChatGPT-3.5 后选择的最佳回答,若是满意,还请采纳,谢谢

    1. 问题分析
      • 在Windows系统下,select函数主要用于处理套接字(socket)相关的I/O复用。当你尝试在subprocess.Popen的标准输出和标准错误流上使用select时,可能会遇到这个问题,因为这些流不是套接字,并且在Windows下select的行为与Unix不同。
      • 错误信息OSError: [WinError 10093] 应用程序没有调用WSAStartup,或者WSAStartup失败表明系统期望进行一些与Windows Sockets(Winsock)相关的初始化,但没有正确进行。这是因为select在Windows下默认与Winsock相关联,而你正在错误地将其用于非套接字对象。
    2. 解决方案
      • 方法一:不使用select(推荐)
        • 在Windows下,可以直接从Popen对象的stdoutstderr读取,而不使用select。例如:
    p = subprocess.Popen(pyinstallerPopenCommand,
                         stdout = subprocess.PIPE,
                         stderr = subprocess.PIPE,
                         cwd = popenCwd,
                         shell = True
                         )
    while True:
        out = p.stdout.readline()
        if out == b'':
            break
        self.returnInfo(out.decode("utf - 8").strip())
    err = p.stderr.readline()
    while err:
        self.returnInfo(err.decode("utf - 8").strip())
        err = p.stderr.readline()
    
    • 方法二:使用select的替代方案(在Windows下较复杂)
      • 一种可能的替代方案是使用selectors模块(在Python 3.4+中可用),它提供了一个高级的I/O复用接口,可以在不同的操作系统上更一致地工作。但是,将其用于subprocess.Popen的标准输出和标准错误流仍然不是一个简单的任务,并且可能需要更多的适配工作。例如:
    import selectors
    import subprocess
    
    sel = selectors.DefaultSelector()
    
    p = subprocess.Popen(pyinstallerPopenCommand,
                         stdout = subprocess.PIPE,
                         stderr = subprocess.PIPE,
                         cwd = popenCwd,
                         shell = True
                         )
    sel.register(p.stdout, selectors.EVENT_READ)
    sel.register(p.stderr, selectors.EVENT_READ)
    
    while True:
        events = sel.select()
        for key, mask in events:
            if key.fileobj is p.stdout:
                line = key.fileobj.readline()
                if line:
                    self.returnInfo(line.decode("utf - 8").strip())
                else:
                    sel.unregister(p.stdout)
            elif key.fileobj is p.stderr:
                line = key.fileobj.readline()
                if line:
                    self.returnInfo(line.decode("utf - 8").strip())
                else:
                    sel.unregister(p.stderr)
        if not sel.get_map():
            break
    
    • 这种方法相对复杂,并且在处理subprocess.Popen的输出时可能会有一些边缘情况需要处理。在大多数情况下,第一种方法(不使用select)在Windows下处理subprocess输出是更简单和可靠的选择。
    评论

报告相同问题?

问题事件

  • 创建了问题 10月27日