pty — Dienstprogramme für Pseudo-Terminals¶
Quellcode: Lib/pty.py
Das Modul pty definiert Operationen zur Handhabung des Pseudo-Terminal-Konzepts: Starten eines anderen Prozesses und die Möglichkeit, programmgesteuert mit dessen steuerndem Terminal zu schreiben und davon zu lesen.
Verfügbarkeit: Unix.
Die Pseudo-Terminal-Handhabung ist stark plattformabhängig. Dieser Code wurde hauptsächlich unter Linux, FreeBSD und macOS getestet (er sollte auch auf anderen POSIX-Plattformen funktionieren, wurde aber nicht gründlich getestet).
Das Modul pty definiert die folgenden Funktionen
- pty.fork()¶
Fork. Verbindet das steuernde Terminal des Kindprozesses mit einem Pseudo-Terminal. Der Rückgabewert ist
(pid, fd). Beachten Sie, dass das Kind pid 0 erhält und fd *ungültig* ist. Der Rückgabewert des Elternprozesses ist die pid des Kindes, und fd ist ein Dateideskriptor, der mit dem steuernden Terminal des Kindes (und auch mit der Standardeingabe und -ausgabe des Kindes) verbunden ist.Warnung
Unter macOS ist die Verwendung dieser Funktion unsicher, wenn sie mit höherstufigen System-APIs gemischt wird, wozu auch die Verwendung von
urllib.requestgehört.
- pty.openpty()¶
Öffnet ein neues Pseudo-Terminal-Paar, wobei nach Möglichkeit
os.openpty()verwendet wird, oder Emulationscode für allgemeine Unix-Systeme. Gibt ein Paar von Dateideskriptoren(master, slave)für das Master- und das Slave-Ende zurück.
- pty.spawn(argv[, master_read[, stdin_read]])¶
Startet einen Prozess und verbindet dessen steuerndes Terminal mit der Standard-I/O des aktuellen Prozesses. Dies wird oft verwendet, um Programme zu täuschen, die darauf bestehen, vom steuernden Terminal zu lesen. Es wird erwartet, dass der hinter dem pty gestartete Prozess schließlich beendet wird, und wenn dies geschieht, gibt spawn zurück.
Eine Schleife kopiert die STDIN des aktuellen Prozesses zum Kind und die vom Kind empfangenen Daten zur STDOUT des aktuellen Prozesses. Dem Kind wird nicht signalisiert, wenn die STDIN des aktuellen Prozesses geschlossen wird.
Die Funktionen master_read und stdin_read erhalten einen Dateideskriptor, von dem sie lesen sollen, und sollten immer eine Byte-Zeichenkette zurückgeben. Um spawn vor dem Beenden des Kindprozesses zur Rückkehr zu zwingen, sollte ein leerer Byte-Array zurückgegeben werden, um das Ende der Datei zu signalisieren.
Die Standardimplementierung für beide Funktionen liest und gibt jedes Mal bis zu 1024 Bytes zurück, wenn die Funktion aufgerufen wird. Der Callback master_read erhält den Master-Dateideskriptor des Pseudo-Terminals, um die Ausgabe des Kindprozesses zu lesen, und stdin_read erhält den Dateideskriptor 0, um von der Standardeingabe des Elternprozesses zu lesen.
Die Rückgabe einer leeren Byte-Zeichenkette von einem der Callbacks wird als End-of-File (EOF) interpretiert, und dieser Callback wird danach nicht mehr aufgerufen. Wenn stdin_read EOF signalisiert, kann das steuernde Terminal nicht mehr mit dem Elternprozess ODER dem Kindprozess kommunizieren. Es sei denn, der Kindprozess beendet sich ohne Eingabe, dann wird spawn für immer in der Schleife bleiben. Wenn master_read EOF signalisiert, tritt das gleiche Verhalten ein (zumindest unter Linux).
Gibt den Exit-Statuswert von
os.waitpid()für den Kindprozess zurück.os.waitstatus_to_exitcode()kann verwendet werden, um den Exit-Status in einen Exit-Code umzuwandeln.Löst ein Auditing-Ereignis
pty.spawnmit dem Argumentargvaus.Geändert in Version 3.4:
spawn()gibt nun den Statuswert vonos.waitpid()für den Kindprozess zurück.
Beispiel¶
Das folgende Programm verhält sich wie der Unix-Befehl script(1) und verwendet ein Pseudo-Terminal, um die gesamte Ein- und Ausgabe einer Terminal-Sitzung in einem „TypeScript“ aufzuzeichnen.
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)