【问题标题】:Continous output with asyncio subprocess使用 asyncio 子进程连续输出
【发布时间】:2022-11-05 04:39:50
【问题描述】:

我正在使用 asyncio 子进程来执行子命令。我想查看长时间运行的过程并将内容同时保存到缓冲区以供以后使用。此外,我发现了这个相关问题 (Getting live output from asyncio subprocess),但它主要围绕 ssh 的用例。

asyncio 子流程文档有一个逐行读取输出的示例,这朝着我想要实现的方向发展。 (https://docs.python.org/3/library/asyncio-subprocess.html#examples)

import asyncio
import sys

async def get_date():
    code = 'import datetime; print(datetime.datetime.now())'

    # Create the subprocess; redirect the standard output
    # into a pipe.
    proc = await asyncio.create_subprocess_exec(
        sys.executable, '-c', code,
        stdout=asyncio.subprocess.PIPE)

    # Read one line of output.
    data = await proc.stdout.readline()
    line = data.decode('ascii').rstrip()

    # Wait for the subprocess exit.
    await proc.wait()
    return line

date = asyncio.run(get_date())
print(f"Current date: {date}")

我将此示例改编为以下内容:

async def subprocess_async(cmd, **kwargs):
    cmd_list = shlex.split(cmd)
    proc = await asyncio.create_subprocess_exec(
            *cmd_list,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT, **kwargs)

    full_log = ""
    while True:
        buf = await proc.stdout.readline()
        if not buf:
            break
        full_log += buf.decode()
        print(f' {buf.decode().rstrip()}')
    await proc.wait()
    res = subprocess.CompletedProcess(cmd, proc.returncode,  stdout=full_log.encode(), stderr=b'')
    return res


这里的问题是,proc.returncode 值有时会变为None。我想,我有一个误解,proc.wait() 的工作原理以及何时可以安全地停止读取输出。如何使用 asyncio 子进程实现连续输出?

【问题讨论】:

  • 你能发布一个proc.returncode 变成None 的例子吗?

标签: python logging subprocess python-asyncio


【解决方案1】:

你的代码对我来说工作正常。您尝试运行什么命令导致您的问题?

我能想到的有两件事可以提供帮助,是

  1. 不要在之后调用 .wait(),而是将返回码设置为循环条件以继续运行。
  2. 不要等待完整的行返回,以防程序类似于ffmpeg,它会做一些技巧在控制台中粘贴自身而不实际发送换行符。

    示例代码:

    import asyncio, shlex, subprocess, sys
    
    
    async def subprocess_async(cmd, **kwargs):
        cmd_list = shlex.split(cmd)
        proc = await asyncio.create_subprocess_exec(
            *cmd_list,
            stdout=asyncio.subprocess.PIPE,
            stderr=asyncio.subprocess.STDOUT,
            **kwargs)
    
        full_log = b""
        while proc.returncode is None:
            buf = await proc.stdout.read(20)
            if not buf:
                break
            full_log += buf
            sys.stdout.write(buf.decode())
        res = subprocess.CompletedProcess(cmd, proc.returncode,  stdout=full_log, stderr=b'')
        return res
    
    
    if __name__ == '__main__':
        asyncio.run(subprocess_async("ffprobe -i video.mp4"))
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-04-15
    • 2020-11-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多