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

invocation = get_invocation()
master, slave = pty.openpty()
subprocess.call(invocation, stdout=slave)
print string_from_fd(master)

我不知道string_from_fd应该是什么。现在,我有点像
def string_from_fd(fd):
  return os.read(fd, 1000)

它起作用了,但是这个数字看起来很奇怪。我认为产量可以安静大,任何数字都可能不够。我尝试了很多堆栈溢出的解决方案,但没有一个有效(它什么也不打印或永远挂起)。
我不太熟悉文件描述符和所有这些,所以如果我做错了任何澄清都将非常感谢。
谢谢!

最佳答案

这对长输出无效:subprocess.call将在PTY的缓冲区已满时阻塞。这就是为什么subprocess.communicate存在,但这不适用于pTy。
标准/最简单的解决方案是使用外部模块pexpect,它在内部使用pty:例如,

pexpect.spawn("/bin/ls --color=auto").read()

将为您提供带有颜色代码的ls输出。
如果您想坚持使用subprocess,则必须出于上述原因使用subprocess.Popen。您的假设是正确的,通过传递1000,您最多读取1000个字节,因此必须使用循环。os.read如果没有要读取的内容并等待数据出现,则阻塞。关键是如何识别进程何时终止:在这种情况下,您知道不会有更多的数据到达。下一个调用os.read将永远阻塞。幸运的是,操作系统帮助您检测这种情况:如果可以用于写入的伪终端的所有文件描述符都已关闭,则os.read将返回空字符串或返回错误,具体取决于操作系统。当这种情况发生时,您可以检查此条件并退出循环。现在,理解以下代码的最后一部分是理解open file descriptor和subprocess是如何结合在一起的: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)

关于python - 从pty读取,无休止,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/41921095/

10-15 13:59