Si bien la respuesta de Thomas Dickey es bastante correcta, Stéphane Chazelas mencionó correctamente en un comentario a la respuesta de Dickey que la conversión no está escrita en piedra; Es parte de la disciplina de línea.
De hecho, la traducción es completamente programable.
La página del manual man 3 termios contiene básicamente toda la información pertinente. (El enlace lleva al proyecto de páginas de manual de Linux , que menciona qué características son exclusivas de Linux y cuáles son comunes a POSIX u otros sistemas; siempre revise la sección Conforme a en cada página allí).
Los iflag
atributos del terminal ( old_settings[0]
en el código que se muestra en la pregunta en Python ) tienen tres indicadores relevantes en todos los sistemas POSIXy:
INLCR
: Si está configurado, traduzca NL a CR en la entrada
ICRNL
: Si está configurado (y IGNCR
no está configurado), traduzca CR a NL en la entrada
IGNCR
: Ignorar CR en entrada
Del mismo modo, también hay configuraciones de salida relacionadas ( old_settings[1]
):
OPOST
: Habilita el procesamiento de salida.
OCRNL
: Asigna CR a NL en la salida.
ONLCR
: Asigna NL a CR en la salida. (XSI; no disponible en todos los sistemas POSIX o Single-Unix-Specification).
ONOCR
: Omitir (no generar) CR en la primera columna.
ONLRET
: Saltar (no emitir) CR.
Por ejemplo, podría evitar depender del tty
módulo. La operación "makeraw" simplemente borra un conjunto de banderas (y establece el 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
aunque por razones de compatibilidad, es posible que desee verificar si todas esas constantes existen primero en el módulo termios (si se ejecuta en sistemas que no son POSIX). También puede usar new_settings[6][termios.VMIN]
y new_settings[6][termios.VTIME]
establecer si una lectura se bloqueará si no hay datos pendientes y cuánto tiempo (en un número entero de decisegundos). (Por VMIN
lo general, se establece en 0 y VTIME
en 0 si las lecturas deben regresar de inmediato, o en un número positivo (décima de segundos) cuánto tiempo debe esperar la lectura como máximo).
Como puede ver, lo anterior (y "makeraw" en general) desactiva toda traducción en la entrada, lo que explica el comportamiento que está viendo cat:
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IGNCR
Para obtener un comportamiento normal, simplemente omita las líneas que borran esas tres líneas, y la traducción de entrada no cambia incluso cuando está "sin procesar".
La new_settings[1] = new_settings[1] & ~termios.OPOST
línea deshabilita todo el procesamiento de salida, independientemente de lo que digan los otros indicadores de salida. Puede omitirlo para mantener intacto el procesamiento de salida. Esto mantiene la salida "normal" incluso en modo sin procesar. (No afecta si la entrada se repite automáticamente o no; eso es controlado por el ECHO
cflag in new_settings[3]
.)
Finalmente, cuando se establecen nuevos atributos, la llamada será exitosa si se estableció alguna de las nuevas configuraciones. Si la configuración es delicada, por ejemplo, si solicita una contraseña en la línea de comando, debe obtener la nueva configuración y verificar que los indicadores importantes estén correctamente activados / desactivados, para estar seguro.
Si desea ver la configuración actual de su terminal, ejecute
stty -a
Los indicadores de entrada suelen estar en la cuarta línea, y los indicadores de salida en la quinta línea, con un -
nombre de indicador anterior si el indicador no está establecido. Por ejemplo, la salida podría ser
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
En pseudoterminales y dispositivos USB TTY, la velocidad de transmisión es irrelevante.
Si escribe scripts de Bash que desean leer, por ejemplo, contraseñas, considere el siguiente modismo:
#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0
La EXIT
trampa se ejecuta cada vez que sale el shell. El stty -g
lee la configuración actual del terminal en el inicio de la secuencia de comandos, por lo que los ajustes actuales se restauran cuando las salidas de guión, de forma automática. Incluso puede interrumpir el script con Ctrl+ C, y hará lo correcto. (En algunos casos de esquina con señales, descubrí que el terminal a veces se atasca con la configuración sin formato / no canónica (que requiere que uno escriba reset
+Enter ciegas en el terminal), pero la ejecución stty sane
antes de restaurar la configuración original real lo ha curado todo el tiempo yo. Entonces es por eso que está ahí; una especie de seguridad adicional).
Puede leer las líneas de entrada (sin eco al terminal) utilizando read
bash incorporado, o incluso leer la entrada carácter por carácter utilizando
IFS=$'\0'
input=""
while read -N 1 c ; do
[[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
input="$input$c"
done
Si no establece IFS
ASCII NUL, el read
incorporado consumirá los separadores, por lo que c
estará vacío. Trampa para jugadores jóvenes.