Python:如何查看 pty 对象以避免阻塞?

发布于 2024-11-16 13:03:40 字数 538 浏览 10 评论 0原文

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

I am using pty to read non blocking the stdout of a process like this:

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()

Everything works fine except that the while-loop occasionally blocks. This is due to the fact that the line print stdout.readline() is waiting for something to be read from stdout. But if the program already terminated, my little script up there will hang forever.

My question is: Is there a way to peek into the stdout object and check if there is data available to be read? If this is not the case it should continue through the while-loop where it will discover that the process actually already terminated and break the loop.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

○闲身 2024-11-23 13:03:40

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

Yes, use the select module's poll:

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

and in the while use:

l = q.poll(0)
if not l:
    pass # no input
else:
    pass # there is some input
终止放荡 2024-11-23 13:03:40

select.poll() 答案非常简洁,但在 Windows 上不起作用。以下解决方案是一种替代方案。它不允许您查看标准输出,但提供了 readline() 的非阻塞替代方案,并且基于此答案

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()

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

  1. 解决方案需要 readline (包括基于队列的)总是阻塞。杀死执行 readline 的线程很困难(不可能?)。它仅在创建它的进程完成时被终止,但在输出生成进程被终止时不会被终止。
  2. 正如 anonnn 指出的那样,将低级 fcntl 与高级 readline 调用混合可能无法正常工作。
  3. 使用 select.poll() 很简洁,但根据 python 文档,它在 Windows 上不起作用。
  4. 使用第三方库对于此任务来说似乎有些过分,并且会增加额外的依赖项。

The select.poll() answer is very neat, but doesn't work on Windows. The following solution is an alternative. It doesn't allow you to peek stdout, but provides a non-blocking alternative to readline() and is based on 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()

Other solutions for non-blocking read have been proposed here, but did not work for me:

  1. Solutions that require readline (including the Queue based ones) always block. It is difficult (impossible?) to kill the thread that executes readline. It only gets killed when the process that created it finishes, but not when the output-producing process is killed.
  2. Mixing low-level fcntl with high-level readline calls may not work properly as anonnn has pointed out.
  3. Using select.poll() is neat, but doesn't work on Windows according to python docs.
  4. Using third-party libraries seems overkill for this task and adds additional dependencies.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文