【问题标题】:Python: How to peek into a pty object to avoid blocking?Python:如何窥视 pty 对象以避免阻塞?
【发布时间】:2011-09-19 07:18:09
【问题描述】:

我正在使用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,它会发现进程实际上已经终止并中断循环。

【问题讨论】:

  • 只是一个想法,不知道这是否有效,但是:也许您可以检查stdout.seek(1, os.SEEK_CUR) 是否抛出异常并确定是否有可用数据?
  • @cularis stdout.seek(1, os.SEEK_CUR) 抛出此异常:IOError: [Errno 29] Illegal seek
  • 一般评论:使用p.poll() is not None(对象身份检查)而不是p.poll() != None(相等性检查,不太精确且速度较慢)。
  • 虽然你的循环偶尔会阻塞,但它不应该无限期挂起,因为正在关闭的文件被认为可以被轮询读取。
  • type(p) is subprocess.Popen doc for subprocess.Popen.poll() 是“检查子进程是否已终止。返回返回码属性。”

标签: python stdout popen pty peek


【解决方案1】:

是的,使用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

【讨论】:

  • 我认为它与进程句柄的 poll 方法没有什么不同,即不能解决问题。
  • 进程句柄的 poll 方法检查进程是否结束,这将检查 stdout 是否有输入等待读取。
  • 啊,好的。如果进程轮询仅检查程序是否退出(嗯,我怎么能认为它会做任何其他事情——为了 $deity 它不知道管道),它可能永远不会合理地工作.
【解决方案2】:

select.poll() 答案非常简洁,但不适用于 Windows。以下解决方案是一种替代方案。它不允许您查看标准输出,但提供了 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=(myprocess,)) #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 的解决方案(包括基于队列的解决方案)总是阻塞。很难(不可能?)杀死执行 readline 的线程。它只会在创建它的进程完成时被杀死,但不会在输出生成进程被杀死时被杀死。
  2. 正如 anonnn 指出的那样,将低级 fcntl 与高级 readline 调用混合使用可能无法正常工作。
  3. 使用 select.poll() 很简洁,但根据 python 文档,在 Windows 上不起作用。
  4. 使用第三方库对于这项任务来说似乎有些矫枉过正,并且会增加额外的依赖项。

【讨论】:

  • 什么是dcmpid
  • @PascalVKooten 在剪切粘贴期间,我没有将 dcmpid 重命名为 myprocess。现已修复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-12-29
  • 1970-01-01
  • 2017-08-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多