pty
— 伪终端工具¶
源代码: Lib/pty.py
pty
模块定义了处理伪终端概念的操作:启动另一个进程,并能够以编程方式向其控制终端写入和读取。
可用性: Unix。
伪终端处理高度依赖于平台。此代码主要在 Linux、FreeBSD 和 macOS 上测试过(它应该在其他 POSIX 平台上工作,但尚未经过彻底测试)。
pty
模块定义了以下函数
- pty.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]])¶
派生一个进程,并将其控制终端与当前进程的标准 I/O 连接。这通常用于迷惑那些坚持从控制终端读取的程序。预计通过 pty 派生的进程最终会终止,当它终止时,spawn 将返回。
一个循环将当前进程的 STDIN 复制到子进程,并将从子进程接收到的数据复制到当前进程的 STDOUT。如果当前进程的 STDIN 关闭,则不会向子进程发送信号。
函数 master_read 和 stdin_read 被传入一个文件描述符,它们应该从该描述符读取,并且它们应该始终返回一个字节串。为了强制 spawn 在子进程退出之前返回,应该返回一个空字节数组来表示文件结束。
这两个函数的默认实现将在每次调用时读取并返回最多 1024 字节。master_read 回调函数被传入伪终端的主文件描述符,用于从子进程读取输出;stdin_read 被传入文件描述符 0,用于从父进程的标准输入读取。
从任一回调返回空字节串都将被解释为文件结束 (EOF) 条件,之后将不再调用该回调。如果 stdin_read 发出 EOF 信号,则控制终端无法再与父进程或子进程通信。除非子进程在没有任何输入的情况下退出,否则 spawn 将永远循环。如果 master_read 发出 EOF 信号,也会导致相同的行为(至少在 Linux 上是这样)。
返回子进程的
os.waitpid()
的退出状态值。os.waitstatus_to_exitcode()
可以用于将退出状态转换为退出码。引发一个 审计事件
pty.spawn
,参数为argv
。在 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)