简体   繁体   English

如何从子进程中获取 output 的实时 stream ?

[英]How to get live stream of output from a subprocess?

I'm trying to extract output from a running Python subprocess.我正在尝试从正在运行的 Python 子进程中提取 output 。 For simplicity, I created a C executable that just prints out 0, 1, 2, etc. every second and runs indefinitely.为简单起见,我创建了一个 C 可执行文件,它每秒只打印 0、1、2 等并无限期运行。

The Python script I have, along with a few other variations of a similar idea, all seem to do nothing for 5 minutes and then print a chunk of 300 lines to the terminal all at once.我拥有的 Python 脚本,以及类似想法的其他一些变体,似乎都在 5 分钟内什么都不做,然后一次将一大块 300 行打印到终端。 Instead, I am trying to have it print one line every second.相反,我试图让它每秒打印一行。 Using Python 3.5.使用 Python 3.5。

import subprocess
import os
import sys
from time import sleep 

def start_program():
    args = ['./test_program']
    p = subprocess.Popen(args, stdout=subprocess.PIPE, universal_newlines=True, bufsize=1)
    return p

def print_output(p):
    for line in p.stdout:
        print(line, end='')

def main():
    p = start_program()
    print_output(p)

main()

The problem is that your C program is detecting that the output isn't going to a TTY and is therefore buffering it.问题是您的 C 程序检测到 output 不会进入 TTY,因此正在缓冲它。 I assume your C program is something like this:我假设您的 C 程序是这样的:

#include <stdio.h>
#include <unistd.h>

int main(void) {
    for(int i = 0; i < 300; ++i) {
        printf("%d\n", i);
        sleep(1);
    }
}

There's two ways you can fix it:有两种方法可以修复它:

  1. Edit your C program to do fflush(stdout);编辑您的 C 程序以执行fflush(stdout); each iteration of the loop or setvbuf to disable buffering at the beginning循环的每次迭代或setvbuf在开始时禁用缓冲
  2. Edit your Python program to wrap your C program in stdbuf , like this: args = ['stdbuf', '-oL', './test_program']编辑您的 Python 程序以将您的 C 程序包装在stdbuf中,如下所示: args = ['stdbuf', '-oL', './test_program']

The base problem was described by @Joseph Sible-Reinstate Monica , but you may also face temporary blocking issue. @Joseph Sible-Reinstate Monica描述了基本问题,但您也可能面临临时阻塞问题。 Using process.stdout.readline() or for line in process.stdout: makes the script wait for a new line in process.stdout .使用process.stdout.readline()for line in process.stdout:使脚本等待process.stdout中的新行。

You can receive and process data in real time without waiting by analyzing the availability of data before reading it.通过在读取数据之前分析数据的可用性,您可以实时接收和处理数据,而无需等待。

Main file:主文件:

import subprocess
from time import sleep

process = subprocess.Popen(('python', 'subTest.py'), stdout=subprocess.PIPE)

while True:
    sleep(0.7) #do smth
    bytes_number = process.stdout.seek(0, 2)
    if bytes_number:
        print('number of new bytes: ', bytes_number)
        process.stdout.seek(0, 0)
        print(process.stdout.read(bytes_number))
    if process.poll() is not None and not bytes_number:
        break

print('done!')

If you run another python script instead of C-executable you also need to use stdout.flush() (analog fflush(stdout) in Python).如果您运行另一个 python 脚本而不是 C 可执行文件,您还需要使用stdout.flush() (Python 中的模拟fflush(stdout) )。

Subprocess file:子进程文件:

from sys import stdout
from time import sleep

for i in range(10):
    sleep(0.2) #do smth
    stdout.write(str(i) + '/')
    stdout.flush()

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM