Mentre la risposta di Thomas Dickey è abbastanza corretta, Stéphane Chazelas ha giustamente menzionato in un commento alla risposta di Dickey che la conversione non è messa in pietra; fa parte della disciplina di linea.
In effetti, la traduzione è completamente programmabile.
La pagina man di man 3 termios contiene sostanzialmente tutte le informazioni pertinenti. (Il collegamento porta al progetto delle pagine man di Linux , che menziona quali funzionalità sono solo Linux e quali sono comuni a POSIX o ad altri sistemi; controlla sempre la sezione Conforming to su ciascuna pagina lì.)
Gli iflag
attributi del terminale ( old_settings[0]
nel codice mostrato nella domanda in Python ) hanno tre flag rilevanti su tutti i sistemi POSIXy:
INLCR
: Se impostato, traduci NL in CR in input
ICRNL
: Se impostato (e IGNCR
non impostato), traduci CR in NL in input
IGNCR
: Ignora CR in input
Allo stesso modo, ci sono anche impostazioni di output correlate ( old_settings[1]
):
OPOST
: Abilita l'elaborazione dell'output.
OCRNL
: Mappa CR su NL in uscita.
ONLCR
: Mappa NL a CR in uscita. (XSI; non disponibile in tutti i sistemi POSIX o Single-Unix-Specification.)
ONOCR
: Salta (non emette) CR nella prima colonna.
ONLRET
: Salta (non emette) CR.
Ad esempio, potresti evitare di fare affidamento sul tty
modulo. L'operazione "makeraw" cancella solo una serie di flag (e imposta la CS8
oflag):
import sys
import termios
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
ch = None
try:
new_settings = termios.tcgetattr(fd)
new_settings[0] = new_settings[0] & ~termios.IGNBRK
new_settings[0] = new_settings[0] & ~termios.BRKINT
new_settings[0] = new_settings[0] & ~termios.PARMRK
new_settings[0] = new_settings[0] & ~termios.ISTRIP
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.IGNCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IXON
new_settings[1] = new_settings[1] & ~termios.OPOST
new_settings[2] = new_settings[2] & ~termios.CSIZE
new_settings[2] = new_settings[2] | termios.CS8
new_settings[2] = new_settings[2] & ~termios.PARENB
new_settings[3] = new_settings[3] & ~termios.ECHO
new_settings[3] = new_settings[3] & ~termios.ECHONL
new_settings[3] = new_settings[3] & ~termios.ICANON
new_settings[3] = new_settings[3] & ~termios.ISIG
new_settings[3] = new_settings[3] & ~termios.IEXTEN
termios.tcsetattr(fd, termios.TCSANOW, new_settings)
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return ch
anche se per motivi di compatibilità, potresti voler verificare se tutte quelle costanti esistono prima nel modulo termios (se si esegue su sistemi non POSIX). Puoi anche usare new_settings[6][termios.VMIN]
e new_settings[6][termios.VTIME]
impostare se una lettura si bloccherà se non ci sono dati in sospeso e per quanto tempo (in numero intero di decisecondi). (In genere VMIN
è impostato su 0 e VTIME
su 0 se le letture devono tornare immediatamente o su un numero positivo (decimo di secondo) per quanto tempo la lettura dovrebbe attendere al massimo.)
Come puoi vedere, quanto sopra (e "makeraw" in generale) disabilita tutte le traduzioni sull'input, il che spiega il comportamento che sta osservando il gatto:
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IGNCR
Per ottenere un comportamento normale, è sufficiente omettere le righe che cancellano quelle tre righe e la traduzione di input rimane invariata anche se "non elaborata".
La new_settings[1] = new_settings[1] & ~termios.OPOST
linea disabilita tutta l'elaborazione dell'output, indipendentemente da ciò che dicono gli altri flag di output. Puoi semplicemente ometterlo per mantenere intatta l'elaborazione dell'output. Ciò mantiene l'output "normale" anche in modalità raw. (Non influisce sul fatto che l'ingresso venga ripetuto automaticamente o meno; ciò è controllato dal ECHO
cflag in new_settings[3]
.)
Infine, quando vengono impostati nuovi attributi, la chiamata avrà esito positivo se sono state impostate una delle nuove impostazioni. Se le impostazioni sono sensibili, ad esempio se si richiede una password dalla riga di comando, è necessario ottenere le nuove impostazioni e verificare che i flag importanti siano impostati / disinseriti correttamente, per essere sicuri.
Se si desidera visualizzare le impostazioni correnti del terminale, eseguire
stty -a
I flag di input sono in genere sulla quarta riga e i flag di output sulla quinta riga, con un -
precedente il nome del flag se il flag non è impostato. Ad esempio, l'output potrebbe essere
speed 38400 baud; rows 58; columns 205; line = 0;
intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = M-^?; eol2 = M-^?; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;
-parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts
-ignbrk brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc ixany imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
Su pseudoterminali e dispositivi USB TTY, la velocità di trasmissione è irrilevante.
Se scrivi script Bash che desiderano leggere ad esempio password, considera il seguente linguaggio:
#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0
La EXIT
trap viene eseguita ogni volta che la shell esce. La stty -g
legge le impostazioni correnti del terminale all'inizio dello script, in modo che le impostazioni correnti vengono ripristinate quando lo script si chiude, automaticamente. Puoi anche interrompere lo script con Ctrl+ C, e farà la cosa giusta. (In alcuni casi angolari con segnali, ho scoperto che il terminale a volte si blocca con le impostazioni raw / non canoniche (che richiedono uno per digitare reset
+ Enterciecamente al terminale), ma l'esecuzione stty sane
prima di ripristinare le impostazioni originali effettive ha curato ogni volta Io. Ecco perché è lì; una sorta di maggiore sicurezza.)
Puoi leggere le righe di input (non copiate sul terminale) usando read
bash incorporato, o persino leggere l'input carattere per carattere usando
IFS=$'\0'
input=""
while read -N 1 c ; do
[[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
input="$input$c"
done
Se non si imposta IFS
ASCII NUL, il read
built-in consumerà i separatori, quindi c
sarà vuoto. Trappola per giovani giocatori.