subprocess.Popen:分离stdout / stderr但保持顺序

时间:2016-01-12 16:30:18

标签: python subprocess

如何使用subprocess.Popen从命令获取输出并为stdout和stderr分别进行回调,但是确保按照流程来自流程的顺序调用这些回调?

如果我不关心将STDOUT和STDERR分开,那么我可以这样做:

fd = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT )
line = fd.stdout.readline()
while line :
    callback( line )
    line = fd.stdout.readline()

但是,如果我有stdoutCallbackstderrCallback,并希望在适当的输出上调用它们,但是按照与上面代码相​​同的顺序调用callback,我该怎么办?这样做呢?

3 个答案:

答案 0 :(得分:1)

fd = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
output,error = fd.communicate()

使用communicate

答案 1 :(得分:1)

所以我认为我已经完成了自己的几个主题。

对于下面的示例,test.py就是:

#!/usr/bin/python -u

import sys
import time

sys.stdout.write("stdout 1\n")
time.sleep(1)
sys.stderr.write("stderr 2\n")
time.sleep(1)
sys.stdout.write("stdout 3\n")
time.sleep(1)
sys.stderr.write("stderr 4\n")
time.sleep(1)

获取正确输出的代码是:

#!/usr/bin/env python

import subprocess
from threading import Thread, Lock

cmdOutput = []
cmdOutputLock = Lock()
STDOUT = 1
STDERR = 2

def _outputLoop( fd, identifier ) :
    line = fd.readline()
    while line :
        cmdOutputLock.acquire()
        cmdOutput.append( ( line, identifier ) )
        cmdOutputLock.release()
        line = fd.readline()

p = subprocess.Popen( "test.py",
                      stdout = subprocess.PIPE,
                      stderr = subprocess.PIPE )

Thread( target=_outputLoop, args=( p.stdout, STDOUT ) ).start()
Thread( target=_outputLoop, args=( p.stderr, STDERR ) ).start()

while fd.poll() is None or cmdOutput :
    output = None
    cmdOutputLock.acquire()
    if cmdOutput :
        output = cmdOutput[0]
        del cmdOutput[0]
    cmdOutputLock.release()

    if output :
        if output[1] == STDOUT :
            print "STDOUT : {}".format( output[0].rstrip() )
        elif output[1] == STDERR :
            print "STDERR : {}".format( output[0].rstrip() )

我绝对可以想象一下stderr线与stdout线混合在一起的时间,但是为了我想要的东西,它肯定有效。 (我把它作为日志模块的一部分,它将运行命令并为stdout和stderr使用不同的日志级别。)

答案 2 :(得分:1)

这是不可能的。如果对不同的文件执行write,则没有定义订单。

如果将write转到stdout,stderr转到同一个地方(例如stdout=PIPE, stderr=STDOUT案例中),则可以获得正确的订单。

如果“近似”订单足够;这是一个simple code example with threads,这里是single-threaded version with a select-loop