在python中,如何检查subprocess.Popen对象中的stdout以查找要读取的内容?

时间:2011-08-08 16:36:21

标签: python subprocess stdout popen readline

在python中,如何检查subprocess.Popen对象中的stdout以查找任何内容?我正在编写一个有时会运行数小时的工具包装器。在子进程的stdout上使用.readline()会在运行超过几分钟时严重降低脚本的速度。如果有什么需要阅读,我需要一种更有效地检查标准输出的方法。顺便说一下,这个特殊工具一次只能写出完整的行。脚本是这样的:

    #!/usr/bin/python -u
    #thiswrap.py

    import sys, time
    from subprocess import *

    chldp = Popen(sys.argv[1], bufsize=0, stdout=PIPE, close_fds=True)
    chstdin,chstdout=chldp.stdin,chldp.stdout
    startnoti=False

    while not chldp.poll():
        rrl=chstdout.readline() # <--- this is where the problem is
        if rrl[-8:]=='REDACTED TEXT':
            sys.stdout.write(rrl[:-1]+'   \r')
            if not startnoti: startnoti=True
        else:
            if startnoti: sys.stdout.write('\n')
            sys.stdout.write(rrl)
            if startnoti: # REDACTED
            time.sleep(0.1)
        time.sleep(0.1)

有什么想法吗?

3 个答案:

答案 0 :(得分:4)

您需要将文件描述符设置为非阻止,您可以使用fcntl执行此操作:

import sys, time, fcntl, os
from subprocess import *

chldp = Popen(sys.argv[1], bufsize=0, stdout=PIPE, close_fds=True)
chstdin, chstdout = chldp.stdin, chldp.stdout
fl = fcntl.fcntl(chstdout, fcntl.F_GETFL)
fcntl.fcntl(chstdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)

while chldp.poll() is not None:
    try:
        rrl = chstdout.readline()
    except IOError:
        time.sleep(0.1)
        continue
    # use rrl

如果没有可用数据,则IOError会引发readline()

请注意,由于chldp.poll()可以在子流程完成时返回0,因此您应该在childp.poll() is not None而不是while中使用not childp.poll()

答案 1 :(得分:1)

可悲的是,没有现成的方法来轮询一个条件“管道中有足够的数据和换行符,以便readline()立即返回”。

如果您想要一次一行,并且不想阻止,您可以:

通过类或生成器实现自己的缓冲并通过它进行轮询,例如:

def linereader():
    data = ""
    while True:
        if poll(f.fd):
            data += f.read(100)
        lines = data.split("\n")
        data = lines[-1]
        for line in lines[:-1]:
            yield line

# use
for line in linereader():
    if line:
       print line
    else:
       time.sleep(...)

或者使用线程(作为练习留给读者,请注意旧版本的python bug,如果你从main以外的线程启动子进程)

答案 2 :(得分:0)

第一条评论中提出的解决方案几乎是正确的。您只需要将整数文件描述符作为第一个参数传递给 fcntl.fcntl,而不是 Python 文件对象。取自 another answer

这是要更改的代码:

chstdout = chldp.stdout
fd = chstdout.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)