pty — 偽終端工具

原始碼: Lib/pty.py


pty 模組定義了處理偽終端概念的操作:啟動另一個程序,並能夠以程式設計方式向其控制終端寫入和讀取。

可用性: Unix。

偽終端處理高度依賴於平臺。此程式碼主要在 Linux、FreeBSD 和 macOS 上測試過(它應該在其他 POSIX 平臺上工作,但尚未經過徹底測試)。

pty 模組定義了以下函式

pty.fork()

派生一個子程序。將子程序的控制終端連線到一個偽終端。返回值是 (pid, fd)。請注意,子程序的 pid 為 0,fd無效的。父程序的返回值是子程序的 pidfd 是一個檔案描述符,連線到子程序的控制終端(以及子程序的標準輸入和輸出)。

警告

在 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_readstdin_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)