pty — 伪终端实用工具

源代码: Lib/pty.py


pty 模块定义了处理伪终端概念的操作:启动另一个进程,并能够以编程方式写入和读取其控制终端。

可用性:Unix。

伪终端处理高度依赖于平台。 此代码主要在 Linux、FreeBSD 和 macOS 上测试(它应该在其他 POSIX 平台上工作,但尚未经过全面测试)。

pty 模块定义了以下函数

pty.fork()

Fork。将子进程的控制终端连接到伪终端。返回值是 (pid, fd)。 请注意,子进程获得的 pid 为 0,并且 fd 是*无效的*。父进程的返回值是子进程的 pid,而 fd 是连接到子进程的控制终端(也连接到子进程的标准输入和输出)的文件描述符。

警告

在 macOS 上,当与使用更高级别的系统 API 混合使用时,使用此函数是不安全的,其中包括使用 urllib.request

pty.openpty()

打开一个新的伪终端对,如果可能的话使用 os.openpty(),或者为通用的 Unix 系统使用模拟代码。返回一对文件描述符 (master, slave),分别用于主端和从端。

pty.spawn(argv[, master_read[, stdin_read]])

生成一个进程,并将其控制终端与当前进程的标准 io 连接。 这通常用于迷惑那些坚持从控制终端读取的程序。预计在 pty 后生成的进程最终会终止,当它终止时 spawn 将返回。

一个循环将当前进程的 STDIN 复制到子进程,并将从子进程接收的数据复制到当前进程的 STDOUT。如果当前进程的 STDIN 关闭,则不会向子进程发出信号。

函数 master_readstdin_read 传递一个它们应该从中读取的文件描述符,并且它们应该始终返回一个字节字符串。为了强制 spawn 在子进程退出之前返回,应返回一个空字节数组以表示文件结束。

这两个函数的默认实现每次调用该函数时都会读取并返回最多 1024 个字节。 master_read 回调传递伪终端的主文件描述符以读取子进程的输出,而 stdin_read 传递文件描述符 0,以从父进程的标准输入读取。

从任何一个回调返回一个空字节字符串都被解释为文件结束 (EOF) 条件,并且在该条件之后不会调用该回调。 如果 stdin_read 发出 EOF 信号,则控制终端将无法再与父进程或子进程通信。除非子进程将在没有任何输入的情况下退出,否则 spawn 将永远循环下去。如果 master_read 发出 EOF 信号,也会导致相同的行为(至少在 Linux 上)。

返回子进程上 os.waitpid() 的退出状态值。

os.waitstatus_to_exitcode() 可用于将退出状态转换为退出代码。

使用参数 argv 引发一个 审核事件 pty.spawn

在 3.4 版本中更改: spawn() 现在返回子进程上 os.waitpid() 的状态值。

示例

以下程序的作用类似于 Unix 命令 script(1),使用伪终端记录终端会话的所有输入和输出,并将其记录在“typescript”中。

import argparse
import os
import pty
import sys
import time

parser = argparse.ArgumentParser()
parser.add_argument('-a', dest='append', action='store_true')
parser.add_argument('-p', dest='use_python', action='store_true')
parser.add_argument('filename', nargs='?', default='typescript')
options = parser.parse_args()

shell = sys.executable if options.use_python else os.environ.get('SHELL', 'sh')
filename = options.filename
mode = 'ab' if options.append else 'wb'

with open(filename, mode) as script:
    def read(fd):
        data = os.read(fd, 1024)
        script.write(data)
        return data

    print('Script started, file is', filename)
    script.write(('Script started on %s\n' % time.asctime()).encode())

    pty.spawn(shell, read)

    script.write(('Script done on %s\n' % time.asctime()).encode())
    print('Script done, file is', filename)