Python:如何窥视pty对象以避免阻塞?

时间:2011-06-21 07:49:11

标签: python stdout popen pty peek

我正在使用pty来读取非阻塞此过程的标准输出:

import os
import pty
import subprocess

master, slave = pty.openpty()

p = subprocess.Popen(cmd, stdout = slave)

stdout = os.fdopen(master)
while True:
    if p.poll() != None:
        break

    print stdout.readline() 

stdout.close()

除了while-loop偶尔阻止外,一切正常。这是因为行print stdout.readline()正在等待从stdout读取内容。但如果程序已经终止,我的小脚本就会永远挂起。

我的问题是:有没有办法窥视stdout对象并检查是否有可供读取的数据?如果不是这种情况,它应该继续通过while-loop,它将发现该进程实际上已经终止并打破了循环。

2 个答案:

答案 0 :(得分:10)

是的,请使用select module's poll

import select
q = select.poll()
q.register(stdout,select.POLLIN)

并且在使用中:

l = q.poll(0)
if not l:
    pass # no input
else:
    pass # there is some input

答案 1 :(得分:2)

select.poll()答案非常简洁,但在Windows上无效。以下解决方案是另一种选择。它不允许你偷看stdout,但提供了readline()的非阻塞替代方案,并且基于this answer

from subprocess import Popen, PIPE
from threading import Thread
def process_output(myprocess): #output-consuming thread
    nextline = None
    buf = ''
    while True:
        #--- extract line using read(1)
        out = myprocess.stdout.read(1)
        if out == '' and myprocess.poll() != None: break
        if out != '':
            buf += out
            if out == '\n':
                nextline = buf
                buf = ''
        if not nextline: continue
        line = nextline
        nextline = None

        #--- do whatever you want with line here
        print 'Line is:', line
    myprocess.stdout.close()

myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process
p1 = Thread(target=process_output, args=(dcmpid,)) #output-consuming thread
p1.daemon = True
p1.start()

#--- do whatever here and then kill process and thread if needed
if myprocess.poll() == None: #kill process; will automatically stop thread
    myprocess.kill()
    myprocess.wait()
if p1 and p1.is_alive(): #wait for thread to finish
    p1.join()

已提出其他非阻塞读取解决方案here,但对我不起作用:

  1. 需要readline的解决方案(包括基于Queue的解决方案)始终会阻止。杀死执行readline的线程很困难(不可能?)它只会在创建它的进程完成时被杀死,但不会在生成输出的进程被终止时被杀死。
  2. 如aonnn所指出的那样,将低级别fcntl与高级别readline调用混合可能无法正常工作。
  3. 使用select.poll()很整洁,但根据python docs在Windows上不起作用。
  4. 使用第三方库对此任务来说似乎有点过分,并添加了其他依赖项。