【问题标题】:In python, How do I check the stdout from a subprocess.Popen object for anything to read?在 python 中,如何检查来自 subprocess.Popen 对象的标准输出是否有任何要读取的内容?
【发布时间】:2011-10-22 13:24:52
【问题描述】:

在 python 中,如何检查来自 subprocess.Popen 对象的标准输出是否有任何要读取的内容?我正在围绕一个有时连续运行数小时的工具编写一个包装器。当运行超过几分钟时,在子进程的标准输出上使用 .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)

有什么想法吗?

【问题讨论】:

  • 为什么让readline屏蔽会出问题?你为什么打电话给sleep
  • 我将忽略有关 readline 阻塞的巨魔部分,并且 sleep 实际上只是一种权宜之计,直到我可以解决 readline 问题。我知道这有点懒惰和笨拙,但我不需要在代码的那部分中添加任何其他内容,除非它可能来自更好的解决方案来知道何时使用 readline() 所以它一直存在直到这个问题消失离开。

标签: python subprocess stdout popen readline


【解决方案1】:

您需要将文件描述符设置为非阻塞,您可以使用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,因此您可能应该在while 中使用childp.poll() is not None 而不是not childp.poll()

【讨论】:

  • 请注意:这在 Windows 上不起作用(我知道海报似乎使用的是 Linux),请参阅 stackoverflow.com/questions/375427/…
  • 此解决方案不起作用。这导致包装器不仅错过了行,而且无论如何都会抛出 IOErrors。此外,事情不可避免地会在开始后的几分钟内崩溃。 (顺便说一句,不是 linux,mac os 10.6 大声笑)。此外,子进程永远不会自行退出,所以我保持 while 循环不变。
【解决方案2】:

遗憾的是,没有现成的方法来轮询条件“管道中有足够的数据并带有换行符,因此 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 错误)

【讨论】:

    【解决方案3】:

    第一条评论中提出的解决方案几乎是正确的。您只需将整数文件描述符作为第一个参数传递给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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-02-03
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多