pty
--- 偽終端工具?
源代碼: Lib/pty.py
pty
模塊定義了一些處理“偽終端”概念的操作:?jiǎn)?dòng)另一個(gè)進(jìn)程并能以程序方式在其控制終端中進(jìn)行讀寫。
偽終端處理高度依賴于具體平臺(tái)。 此代碼主要針對(duì) Linux, FreeBSD 和 macOS 進(jìn)行了測(cè)試(它應(yīng)當(dāng)也能在其他 POSIX 平臺(tái)上工作,但是未經(jīng)充分測(cè)試)。
pty
模塊定義了下列函數(shù):
- pty.fork()?
分叉。 將子進(jìn)程的控制終端連接到一個(gè)偽終端。 返回值為
(pid, fd)
。 請(qǐng)注意子進(jìn)程獲得 pid 0 而 fd 為 invalid。 父進(jìn)程返回值為子進(jìn)程的 pid 而 fd 為一個(gè)連接到子進(jìn)程的控制終端(并同時(shí)連接到子進(jìn)程的標(biāo)準(zhǔn)輸入和輸出)的文件描述符。
- pty.openpty()?
打開(kāi)一個(gè)新的偽終端對(duì),如果可能將使用
os.openpty()
,或是針對(duì)通用 Unix 系統(tǒng)的模擬代碼。 返回一個(gè)文件描述符對(duì)(master, slave)
,分別表示主從兩端。
- pty.spawn(argv[, master_read[, stdin_read]])?
生成一個(gè)進(jìn)程,并將其控制終端連接到當(dāng)前進(jìn)程的標(biāo)準(zhǔn) io。 這常被用來(lái)應(yīng)對(duì)堅(jiān)持要從控制終端讀取數(shù)據(jù)的程序。 在 pty 背后生成的進(jìn)程預(yù)期最后將被終止,而且當(dāng)它被終止時(shí) spawn 將會(huì)返回。
將當(dāng)前進(jìn)程的 STDIN 拷貝到子進(jìn)程并將從子進(jìn)程接收的數(shù)據(jù)拷貝到當(dāng)前進(jìn)程的 STDOUT 的循環(huán)。 如果當(dāng)前進(jìn)程的 STDIN 關(guān)閉則它不會(huì)向子進(jìn)程發(fā)信號(hào)。
master_read 和 stdin_read 函數(shù)會(huì)被傳入一個(gè)文件描述符供它們讀取內(nèi)容,并且它們總是應(yīng)當(dāng)返回一個(gè)字節(jié)串。 為了強(qiáng)制 spawn 在子進(jìn)程退出之前返回,應(yīng)當(dāng)返回一個(gè)空字節(jié)數(shù)組來(lái)提示文件的結(jié)束。
兩個(gè)函數(shù)的默認(rèn)實(shí)現(xiàn)在每次函數(shù)被調(diào)用時(shí)將讀取并返回至多 1024 個(gè)字節(jié)。 會(huì)向 master_read 回調(diào)傳入偽終端的主文件描述符以從子進(jìn)程讀取輸出,而向 stdin_read 傳入文件描述符 0 以從父進(jìn)程的標(biāo)準(zhǔn)輸入讀取數(shù)據(jù)。
從兩個(gè)回調(diào)返回空字節(jié)串會(huì)被解讀為文件結(jié)束 (EOF) 條件,在此之后回調(diào)將不再被調(diào)用。 如果 stdin_read 發(fā)出 EOF 信號(hào)則控制終端就不能再與父進(jìn)程或子進(jìn)程進(jìn)行通信。 除非子進(jìn)程將不帶任何輸入就退出,否則隨后 spawn 將一直循環(huán)下去。 如果 master_read 發(fā)出 EOF 信號(hào)則會(huì)有相同的行為結(jié)果(至少是在 Linux 上)。
從子進(jìn)程中的
os.waitpid()
返回退出狀態(tài)值。可以使用
waitstatus_to_exitcode()
來(lái)將退出狀態(tài)轉(zhuǎn)換為退出碼。引發(fā)一個(gè) 審計(jì)事件
pty.spawn
,附帶參數(shù)argv
。在 3.4 版更改:
spawn()
現(xiàn)在會(huì)從子進(jìn)程的os.waitpid()
返回狀態(tài)值。
示例?
以下程序的作用類似于 Unix 命令 script(1),它使用一個(gè)偽終端來(lái)記錄一個(gè) "typescript" 里終端進(jìn)程的所有輸入和輸出:
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)