Python 阅读pty,无需无休止的绞刑

Python 阅读pty,无需无休止的绞刑,python,subprocess,pty,Python,Subprocess,Pty,我有一个脚本,如果在tty上,它会打印彩色输出。它们并行执行,所以我不能把它们的stdout设置为tty。我也不能控制脚本代码(强制着色),所以我想通过pty来伪造它。我的代码: invocation = get_invocation() master, slave = pty.openpty() subprocess.call(invocation, stdout=slave) print string_from_fd(master) 而且我不知道,从_fd中的string\u应该是什么。现

我有一个脚本,如果在tty上,它会打印彩色输出。它们并行执行,所以我不能把它们的stdout设置为tty。我也不能控制脚本代码(强制着色),所以我想通过pty来伪造它。我的代码:

invocation = get_invocation()
master, slave = pty.openpty()
subprocess.call(invocation, stdout=slave)
print string_from_fd(master)
而且我不知道,从_fd中的
string\u应该是什么。现在,我有点像

def string_from_fd(fd):
  return os.read(fd, 1000)
这是可行的,但这个数字
1000
看起来很奇怪。我认为产出可能非常大,任何数量都不足以满足需求。我尝试了很多来自堆栈溢出的解决方案,但没有一个有效(它不会打印任何内容或永远挂起)

我不太熟悉文件描述符和所有这些,所以如果我做错了什么,任何澄清都将不胜感激


谢谢

这对长时间输出不起作用:
子流程。一旦PTY的缓冲区满了,调用将被阻止。这就是存在的原因,但这对PTY不起作用

标准/最简单的解决方案是使用外部模块,内部使用PTY:例如

pexpect.spawn("/bin/ls --color=auto").read()
将为您提供带有颜色代码的
ls
输出

如果您想坚持使用
子流程
,那么出于上述原因,您必须使用
subprocess.Popen
。您的假设是正确的,通过传递
1000
,您最多读取1000个字节,因此必须使用循环
os.read
在无需读取的情况下阻塞,并等待数据出现。关键在于如何识别进程何时终止:在这种情况下,您知道不再有数据到达。下一次调用
os.read
将永远被阻止。幸运的是,操作系统可以帮助您检测到这种情况:如果所有可用于写入的伪终端的文件描述符都已关闭,则
os.read
将返回空字符串或返回错误,具体取决于操作系统。您可以检查这种情况,并在发生这种情况时退出循环。现在,理解以下代码的最后一步是理解打开的文件描述符和
子进程如何结合在一起:
subprocess.Popen
内部调用
fork()
,它复制当前进程,包括所有打开的文件描述符,然后在两个执行路径中的一个内调用
exec()
,它将终止当前进程以支持新进程。在另一个执行路径中,控制返回到Python脚本。因此,在调用
subprocess.Popen
之后,PTY的从属端有两个有效的文件描述符:一个属于派生进程,一个属于Python脚本。如果关闭您的,那么可以用于向主端发送数据的唯一文件描述符属于派生进程。终止后,它关闭,PTY进入主端调用
read
失败的状态

代码如下:

import os
import pty
import subprocess

master, slave = pty.openpty()
process = subprocess.Popen("/bin/ls --color", shell=True, stdout=slave,
                           stdin=slave, stderr=slave, close_fds=True)
os.close(slave)

output = []
while True:
    try:
        data = os.read(master, 1024)
    except OSError:
        break
    if not data:
        break
    output.append(data) # In Python 3, append ".decode()" to os.read()
output = "".join(output)

嗯,我试过了,但没用。当为True时,它将一直运行。
我已经更新了
Popen
调用,使得生成的进程只看到PTY。如果这不能改变事情,那么:你在使用什么操作系统?您确定您尝试运行的脚本终止/不是死锁,因为它需要用户输入吗?我的示例是终止的,还是永远循环?你能想出一个你愿意在循环没有终止的地方共享的命令示例吗?使用哪种Python版本?(为了进行调试,请尝试在
while
循环中添加
打印repr(输出[-1])
。)我正在运行OS X 10.12和Python 2.7.10。即使在您的示例中,
/bin/ls--color
也会出现问题<代码>打印报告(输出[-1])
无休止地写入
'
–只是空字符串。我在循环中设置了一个条件,
t=os.read(master,1024);如果len(t)=0:break
,那么它就是这样工作的。您必须这样做,以确保在子程序退出后,没有打开的文件描述符可用于将数据写入主程序。我已经扩展了答案的这一部分。