【问题标题】:Pipe unbuffered stdout from subprocess to websocket将无缓冲的标准输出从子进程管道传输到 websocket
【发布时间】:2020-08-18 08:49:54
【问题描述】:

如何将标准输出从子进程传送到 websocket 而无需等待换行符? 目前,以下代码仅在换行符上发送标准输出。

为子进程运行的脚本附加的代码。是否没有从那里正确刷新输出?

send_data.py:

import asyncio
import websockets
import subprocess
import sys
import os

async def foo(websocket, path):
        print ("socket open")
        await websocket.send("successfully connected")

        with subprocess.Popen(['sudo','python3', '-u','inline_print.py'],stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0, universal_newlines=True) as p:
                for line in p.stdout:
                    line = str(line.rstrip())
                    await websocket.send(line)
                    p.stdout.flush()
                for line in p.stderr:
                    line = str(line.rstrip())
                    await websocket.send(line)
                    p.stdout.flush()


start_server = websockets.serve(foo, "localhost", 8765)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

inline_print.py:

from time import sleep
import sys

loading = 'LOADING...LOADING...LOADING...LOADING...LOADING...'
for i in range(50):
    print(loading[i], sep='', end=' ', flush=True)
    sleep(0.1)

如果将end=' ' 更改为end='\n',则来自send_data.py 的标准输出会实时发生。

js 客户端:

var ws = new WebSocket('ws://localhost:8765/');

ws.onmessage = function(event) {
  console.log(event.data);
};

我承认这个问题与以下类似:

catching-stdout-in-realtime-from-subprocess

how-do-i-get-real-time-information-back-from-a-subprocess-popen-in-python-2-5

intercepting-stdout-of-a-subprocess-while-it-is-running

如果没有来自子进程的换行符,任何解决方案都无法工作。

【问题讨论】:

    标签: python websocket subprocess stdout


    【解决方案1】:

    如果你写

          for line in p.stdout:
    

    然后你(有点)含蓄地说,你想等待一个完整的行

    您必须使用read(num_bytes) 而不是readline()

    下面一个例子来说明:

    sub.py:(示例子流程)

    import sys, time
    for v in range(20):
        print(".", end="")
        sys.stdout.flush()
        if v % 4 == 0:
            print()
        if v % 3 != 0:
            time.sleep(0.5)
    

    rdunbuf.py:(读取标准输出无缓冲的示例)

    contextlib, time, subprocess
    
    def unbuffered(proc, stream='stdout'):
        stream = getattr(proc, stream)
        with contextlib.closing(stream):
            while True:
                last = stream.read(80) # read up to 80 chars
                # stop when end of stream reached
                if not last:
                    if proc.poll() is not None:
                        break
                else:
                    yield last
    
    # open subprocess without buffering and without universal_newlines=True
    proc = subprocess.Popen(["./sub.py"], stdout=subprocess.PIPE, bufsize=0)
    
    for l in unbuffered(proc):
        print(l)
    print("end")
    

    同时请注意,如果您的代码在产生正常输出之前产生大量错误消息,则可能会阻塞,因为您首先尝试读取所有正常输出,然后才从 stderr 读取数据。

    您应该像在任何管道缓冲区独立阻塞之前一样读取您的子进程生成的任何数据,无论这是 stdout 还是 stderr。 您可以使用 select.select() ( https://docs.python.org/3.8/library/select.html#select.select ) 来决定是否必须从 stdout 或 stderr 读取

    【讨论】:

    • 我注意到我还需要删除 universal_newlines=True
    • OK 将添加到我的答案中
    猜你喜欢
    • 2019-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-16
    • 2013-06-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多