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]])¶
生成一个进程,并将它的控制终端与当前进程的标准 IO 连接起来。这通常用于迷惑坚持从控制终端读取数据的程序。预计在 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),使用伪终端将终端会话的所有输入和输出记录在“脚本”中。
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)