简体   繁体   English

如何从“子进程”流式输出(一次一行)?

[英]How do I stream output (one line at a time) from `subprocess`?

I'm really struggling to successfully stream output from a bash command in my Python script using the subprocess package.我真的很难使用subprocess包在我的 Python 脚本中成功地从 bash 命令流输出。 The command is just a simple AWS CLI command to upload an object to S3:该命令只是一个简单的AWS CLI命令,用于将对象上传到 S3:

aws s3 cp --profile MY-PROFILE UPLOADED_FILE s3://BUCKET/PREFIX/

When I run this in bash , it shows the progress of the upload.当我在bash运行它时,它会显示上传的进度。 With subprocess , I can only get it working in the following ways:使用subprocess ,我只能通过以下方式使其工作:

  • Final output only at the end.最终输出仅在最后。

     upload: ./OBJECT to s3://BUCKET/PREFIX/OBJECT
  • Line by line output逐行输出

    Completed 512.0 KiB/12.2 MiB (697.8 KiB/s) with 1 file(s) remaining Completed 768.0 KiB/12.2 MiB (980.0 KiB/s) with 1 file(s) remaining Completed 1.0 MiB/12.2 MiB (1.2 MiB/s) with 1 file(s) remaining Completed 1.2 MiB/12.2 MiB (1.5 MiB/s) with 1 file(s) remaining Completed 1.5 MiB/12.2 MiB (1.6 MiB/s) with 1 file(s) remaining Completed 1.8 MiB/12.2 MiB (1.4 MiB/s) with 1 file(s) remaining
  • Similar thing without line separation:类似的东西没有线分离:

     b'Completed 256.0 KiB/12.2 MiB (297.5 KiB/s) with 1 file(s) remaining\\rCompleted 512.0 KiB/12.2 MiB (506.7 KiB/s) with 1 file(s) remaining\\rCompleted 768.0 KiB/12.2 MiB (544.4 KiB/s) with 1 file(s) remaining\\rCompleted 1.0 MiB/12.2 MiB (672.0 KiB/s) with 1 file(s) remaining \\rCompleted 1.2 MiB/12.2 MiB (707.0 KiB/s) with 1 file(s) remaining \\rCompleted 1.5 MiB/12.2 MiB (780.3 KiB/s) with 1 file(s) remaining \\rCompleted 1.8 MiB/12.2 MiB (849.1 KiB/s) with 1 file(s) remaining \\rCompleted 2.0 MiB/12.2 MiB (865.6 KiB/s) with 1 file(s) remaining \\rCompleted 2.2 MiB/12.2 MiB (971.0 KiB/s) with 1 file(s) remaining \\rCompleted 2.5 MiB/12.2 MiB (992.4 KiB/s) with 1 file(s) remaining \\rCompleted 2.8 MiB/12.2 MiB (1.0 MiB/s) with 1 file(s) remaining

What I need is to just use one line and show the live status, just as it would do in bash.我需要的是只使用一行并显示实时状态,就像在 bash 中一样。

Here are a number of different functions I've tried, all with no luck:以下是我尝试过的许多不同功能,但都没有运气:

from subprocess import Popen, PIPE
import subprocess
import os
import shlex
from time import sleep


def stream_process(process):
    go = process.poll() is None
    for line in process.stdout:
        print(line)
    return go

process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
while stream_process(process):
    sleep(0.1)


def run(command):
  with subprocess.Popen(command, text=True, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) as process:
    for line in process.stdout:
      print(line)

run(command)

process = Popen(command, text=True, shell=True, stdout=PIPE, bufsize=1, universal_newlines=True)
while True:
    output = process.stdout.readline()
    if process.poll() is not None and output == b'':
        break
    if output:
        print (output.strip())
retval = process.poll()


with Popen(command, text=True, shell=True, stdout=PIPE, bufsize=1, universal_newlines=True) as p:

    while True: 
        line = p.stdout.readline()
        print(line.strip())


    for line in p.stdout:
        print(line.strip(), "\n", flush=True, end='')
        # print(line.strip())


print(result.stdout.readlines())

print("args", result.args)
print("returncode", result.returncode)
print("stdout", result.stdout)
print("stderr", result.stderr)
print("check_returncode()", result.check_returncode())

def run_command(cmd):
    p = Popen(shlex.split(cmd), bufsize=1, universal_newlines=True)
    return p.poll()

run_command(command)
# invoke process
process = subprocess.Popen(shlex.split(command),shell=False,stdout=subprocess.PIPE)

# Poll process.stdout to show stdout live
while True:
  output = process.stdout.readline()
  if process.poll() is not None:
    break
  if output:
    print(output.strip())
rc = process.poll()


print(result.stdout.readlines())
while True:
    line = result.stdout.readline()
    if not line:
        break
    print(line, flush=True)

FAQ常问问题

Why don't you use boto3 instead of subprocess + bash ?你为什么不使用boto3 ,而不是subprocess + bash

I'm developing a program for non-Python devs, so I'd like to limit dependencies and only use standard packages as much as possible.我正在为非 Python 开发人员开发程序,因此我想限制依赖项,并尽可能只使用标准包。

You simply should to:您只需:

import subprocess

p = subprocess.Popen(("ls"), stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout:
    print(line.decode("utf-8").rstrip())
p.wait()
status = p.poll()
print("process terminate with code: %s" % status)

Result in:造成:

my-file.txt
...
process terminate with code: 0

When the subprocess exit, stdout is closed, which break the loop.当子进程退出时,stdout 关闭,从而中断循环。

subprocess stdout is a bytes stream.子进程标准输出是一个字节流。 If you need str objects, you should decode it: line.decode("utf-8") .如果你需要 str 对象,你应该解码它: line.decode("utf-8")

But I think this is not the problem.但我认为这不是问题。 Some command have different output in case of stdout is a terminal or a pipe.在 stdout 是终端或管道的情况下,某些命令具有不同的输出。 Like with wget .就像wget一样。 I you want a progress bar, you have to parse output and implement your own progress bar.我想要一个进度条,你必须解析输出并实现你自己的进度条。

for line in process.stdout and process.stdout.readline() are not fit for the task because they don't return before a whole line has ended, ie a \\n is received, while a progress part only ends with \\r . for line in process.stdoutprocess.stdout.readline()不适合该任务,因为它们在整行结束之前不会返回,即收到\\n ,而进度部分仅以\\r结尾。 A way to read output independently of any line termination is to use os.read() :独立于任何行终止读取输出的一种方法是使用os.read()

from os         import read

process = Popen(shlex.split(cmd), stdout=PIPE)
while buffer := read(process.stdout.fileno(), 4096):
    print(buffer.decode(), end='')

As xrisk noted, using text=True would allow lines ending with \\r to be read with readline() , but that's of no use here because therewith all line endings in the output will be converted to '\\n' .正如 xrisk 指出的那样,使用text=True将允许使用readline()读取以\\r结尾的行,但这在这里没有用,因为输出中所有行结尾都将转换为'\\n'

But there's indeed a way to use a text stream without loosing the original line endings by means of io.TextIOWrapper - therewith we can do this:但是确实有一种方法可以通过io.TextIOWrapper在不丢失原始行结尾的情况下使用文本流 - 我们可以这样做:

import io

process = Popen(shlex.split(cmd), stdout=PIPE)
for line in io.TextIOWrapper(process.stdout, newline=""): print(line, end="")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何从子流程输出中仅获取特定的一行 - How to get only one specific line from subprocess output 如何从子进程中获取 output 的实时 stream ? - How to get live stream of output from a subprocess? 我如何将 stream 输入到 python Popen 子进程和 stream Z78E6221F6393D1356681DB93 中? - How can I stream input into a python Popen subprocess and stream output from it? 是否可以实时将python子流程的输出流式传输到网页? - Is it possible to stream output from a python subprocess to a webpage in real time? 如何隐藏子流程的输出并继续执行脚本? - How do I hide the output from a subprocess and continue the script execution? 如何为subprocess.call创建自定义输出流 - How does one create custom output stream for subprocess.call 如何在一行上输出首字母缩略词 - How do I output the acronym on one line 如何等待子进程完成,存储其输出并在输出中添加前缀(没有新行)? - How do I wait for a subprocess to finish, store it's output and add a prefix (without a new line) to the output? 如果子进程是Java程序(带有命令行参数),如何从subprocess.check_output获取输出? - How can I get output from subprocess.check_output if the subprocess is a java program(with Command-line arguments)? 如何抑制subprocess.call中的输出,但保存输出? - How do I suppress the output in subprocess.call, but save the output?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM