如何为subprocess.call创建自定义输出流

时间:2017-09-13 15:28:29

标签: python python-3.x io subprocess

我试图通过定义自己的输出流来获得subprocess.call的实时输出,但它似乎不起作用。

原因:我想运行一个子进程,并将该调用输出到两个stdout(实时,以便我可以查看脚本并查看当前进度)以及将其记录到文件

subprocess.py:

 > show dbs
 Wed Sep 13 11:18:32.964 listDatabases failed:{ "ok" : 0, "errmsg" : "unauthorized" } at src/mongo/shell/mongo.js:46
 > 

mainprocess.py

import time

while True:
    print("Things")
    time.sleep(1)

但是当我运行此代码时,我收到此错误消息:

import subprocess
import io

class CustomIO(io.IOBase):
    def write(self, str):
        print("CustomIO: %s"%str)
        # logging to be implemented here

customio = CustomIO()
subprocess.call(["python3", "print_process.py"], stdout=customio)

那么,任何人都有任何线索,如果可能的话?

我是否继承了错误的基类?

我没有超载正确的方法吗?

或者我完全脱离了轨道,应该以完全不同的方式解决这个问题?

2 个答案:

答案 0 :(得分:2)

如果要处理子流程的输出,则需要传递stdout=subprocess.PIPE。但是,call()run()都会等到进程完成后再使其可用,因此您无法使用这些函数实时处理它。

您需要使用subprocess.Popen

import subprocess as sp

def handle_output(output_line):
    ...

my_process = sp.Popen(["python3", "print_process.py"],
                      stdout=sp.PIPE,
                      universal_newlines=True) # changes stdout from bytes to text

for line in my_process.stdout:
    handle_output(line)

my_process.wait()

更新:确保在子进程中刷新输出缓冲区:

while True:
    print("Things", flush=True)
    time.sleep(1)

答案 1 :(得分:1)

您需要使用文件描述符指定和打开流。 fileno未实现io.IOBase,因为这只是一个内存中的流:

  

Frequently Used Arguments

     

stdinstdoutstderr指定已执行程序的标准   输入,标准输出和标准错误文件句柄。   有效值为PIPEDEVNULL,现有文件描述符(a   正整数),现有文件对象和NonePIPE表示   应该创建一个给孩子的新管道。 DEVNULL表示   将使用特殊文件os.devnull。使用默认设置   None,不会发生重定向;

因此您可以将套接字,管道和打开文件用作stdout,文件描述符将作为stdout传递给子进程。我没有使用subprocess.Popen的套接字,但是我希望它们能够工作,我相信这里重要的是子文件的文件描述符,而不是文件描述符所指向的对象类型。