subprocess.Popen处理stdout和stderr

时间:2014-10-14 20:36:16

标签: python subprocess

我正在尝试处理来自stdout调用的stderrsubprocess.Popen,这两个调用都通过subprocess.PIPE捕获,但是想要处理输出(例如打印它们)在终端上)来了。

我见过的所有当前解决方案都会等待Popen调用完成,以确保捕获所有stdoutstderr,以便它可以处理。

这是一个带有混合输出的示例Python脚本,在实时处理(或尽可能实时)时,我似乎无法复制订单:

$ cat mix_out.py

import sys

sys.stdout.write('this is an stdout line\n')
sys.stdout.write('this is an stdout line\n')
sys.stderr.write('this is an stderr line\n')
sys.stderr.write('this is an stderr line\n')
sys.stderr.write('this is an stderr line\n')
sys.stdout.write('this is an stdout line\n')
sys.stderr.write('this is an stderr line\n')
sys.stdout.write('this is an stdout line\n')

似乎可能有用的一种方法是使用线程,因为那时读取将是异步的,并且可以在subprocess产生输出时进行处理。

此正常流程的当前实现stdout首先和stderr最后,如果输出最初在两者之间交替,则可能是欺骗性的:

cmd = ['python', 'mix_out.py']

process = subprocess.Popen(
    cmd,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    close_fds=True,
    **kw
)

if process.stdout:
    while True:
        out = process.stdout.readline()
        if out == '' and process.poll() is not None:
            break
        if out != '':
            print 'stdout: %s' % out
            sys.stdout.flush()

if process.stderr:
    while True:
        err = process.stderr.readline()
        if err == '' and process.poll() is not None:
            break
        if err != '':
            print 'stderr: %s' % err
            sys.stderr.flush()

如果我运行以上(保存为out.py)来处理上面的mix_out.py示例脚本,则按顺序处理流(按预期方式):

$ python out.py
stdout: this is an stdout line
stdout: this is an stdout line
stdout: this is an stdout line
stdout: this is an stdout line
stderr: this is an stderr line
stderr: this is an stderr line
stderr: this is an stderr line
stderr: this is an stderr line

我理解一些系统调用可能会缓冲,我对此感到满意,我要解决的一件事就是在发生流时遵循流的顺序。

是否有办法处理stdoutstderr,因为它来自subprocess ,而不必使用线程? (代码在无法进行线程处理的受限远程系统中执行。)

需要区分stdout和stderr是必须的(如示例输出中所示)

理想情况下,没有额外的库是最好的(例如我知道pexpect解决了这个问题)

很多例子都提到select的使用,但是我没有想出能够保留输出顺序的东西。

3 个答案:

答案 0 :(得分:0)

很抱歉,如果我误解了这个问题...但如果您正在寻找一种方式 subprocess.Popen 实时输出到stdout / stderr ,那么你应该能够实现:

import sys, subprocess
p = subprocess.Popen(cmdline,
                     stdout=sys.stdout,
                     stderr=sys.stderr)

可能stderr=subprocess.STDOUT可能会简化您的过滤?

如果那不是您正在/正在寻找的,抱歉。但希望它能满足其他人的需求。

答案 1 :(得分:0)

我在这里找到example的工作(请参阅capture_together.py的列表)。在Windows和UNIX OS上,混合使用cerrcout作为子进程执行的已编译C ++代码。结果相同

答案 2 :(得分:-1)

我可以使用select.select()

来解决这个问题
process = subprocess.Popen(
    cmd,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    close_fds=True,
    **kw
)

while True:
    reads, _, _ = select(
        [process.stdout.fileno(), process.stderr.fileno()],
        [], []
    )

    for descriptor in reads:
        if descriptor == process.stdout.fileno():
            read = process.stdout.readline()
            if read:
                print 'stdout: %s' % read

        if descriptor == process.stderr.fileno():
            read = process.stderr.readline()
            if read:
                print 'stderr: %s' % read
        sys.stdout.flush()

    if process.poll() is not None:
        break

select()参数(reads的第一个参数)上的文件描述符传递给select()并循环遍历它们(只要process.poll()表示该过程还活着。)

不需要线程。代码改编自此stackoverflow answer