我有一个脚本,如果在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/