Alf*_*ien 2 python subprocess pty
我有一个脚本,如果它在 tty 上,它会打印彩色输出。一堆并行执行,所以我不能把他们的标准输出到 tty。我也无法控制脚本代码(强制着色),所以我想通过 pty.txt 伪造它。我的代码:
invocation = get_invocation()
master, slave = pty.openpty()
subprocess.call(invocation, stdout=slave)
print string_from_fd(master)
Run Code Online (Sandbox Code Playgroud)
我想不通,应该在string_from_fd. 现在,我有类似的东西
def string_from_fd(fd):
return os.read(fd, 1000)
Run Code Online (Sandbox Code Playgroud)
它有效,但这个数字1000看起来很奇怪。我认为输出可以很大,任何数量都可能不够。我从堆栈溢出中尝试了很多解决方案,但没有一个有效(它不打印任何内容或永远挂起)。
我对文件描述符和所有这些都不是很熟悉,所以如果我做错了什么,我将不胜感激。
谢谢!
这不适用于长输出:subprocess.call一旦 PTY 的缓冲区已满,就会阻塞。这就是subprocess.communicate存在的原因,但这不适用于 PTY。
标准/最简单的解决方案是使用外部模块pexpect,它在内部使用 PTY:例如,
pexpect.spawn("/bin/ls --color=auto").read()
Run Code Online (Sandbox Code Playgroud)
将为您ls提供带有颜色代码的输出。
如果您想坚持使用subprocess,那么您必须subprocess.Popen出于上述原因使用。您的假设是正确的,通过传递1000,您最多读取 1000 个字节,因此您必须使用循环。os.read如果没有可读取的内容,则阻塞并等待数据出现。问题在于如何识别进程何时终止:在这种情况下,您知道不会再有数据到达。下一次调用os.read将永远阻塞。幸运的是,操作系统可以帮助您检测这种情况:如果可以用于写入的伪终端的所有文件描述符都已关闭,则os.read将返回空字符串或返回错误,具体取决于操作系统。您可以检查这种情况并在发生这种情况时退出循环。现在理解以下代码的最后一部分是理解打开文件描述符和如何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)
Run Code Online (Sandbox Code Playgroud)