Хотя ответ Томаса Дики совершенно верен, Стефан Шазелас правильно отметил в комментарии к ответу Дики, что преобразование не является чем-то неизменным; это часть линейной дисциплины.
На самом деле, перевод полностью программируется.
Страница man 3 termios man содержит практически всю необходимую информацию. (Ссылка ведет на Linux man-pages project, где упоминается, какие функции присущи только Linux, а какие являются общими для POSIX или других систем; всегда проверяйте раздел Соответствие на каждой странице. )
Атрибуты терминала iflag
(old_settings[0]
в коде, показанном в вопросе в Python) имеют три соответствующих флага на всех POSIXy системах:
INLCR
: Если установлен, переводить NL в CR на входеICRNL
: Если установлено (и IGNCR
не установлено), переводить CR в NL на входеIGNCR
: Игнорировать CR на входеАналогично, есть связанные настройки вывода (old_settings[1]
):
OPOST
: Включить обработку вывода. OCRNL
: Сопоставить CR с NL на выходе. ONLCR
: Сопоставить NL с CR на выходе. (XSI; доступно не во всех системах POSIX или Single-Unix-Specification.)ONOCR
: Пропускать (не выводить) CR в первом столбце. ONLRET
: Пропустить (не выводить) CR. Например, вы можете не полагаться на модуль tty
. Операция "makeraw" просто очищает набор флагов (и устанавливает 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
хотя ради совместимости, возможно, вы захотите сначала проверить, существуют ли все эти константы в модуле termios (если вы работаете на системах не-POSIX). Вы также можете использовать new_settings[6][termios.VMIN]
и new_settings[6][termios.VTIME]
, чтобы установить, будет ли чтение блокироваться, если нет ожидающих данных, и как долго (в целых числах децисекунд). (Обычно VMIN
устанавливается в 0, а VTIME
в 0, если чтение должно вернуться немедленно, или в положительное число (десятые доли секунды), сколько времени чтение должно ждать максимум).
Как вы можете видеть, вышеуказанное (и "makeraw" в целом) отключает всю трансляцию на вводе, что объясняет поведение, наблюдаемое котом:
new_settings[0] = new_settings[0] & ~termios.INLCR
new_settings[0] = new_settings[0] & ~termios.ICRNL
new_settings[0] = new_settings[0] & ~termios.IGNCR
Чтобы получить нормальное поведение, просто опустите строки, очищающие эти три строки, и трансляция ввода не изменится даже при "raw".
Строка new_settings[1] = new_settings[1] & ~termios.OPOST
отключает всю обработку вывода, независимо от того, что говорят другие флаги вывода. Вы можете просто опустить ее, чтобы сохранить обработку вывода без изменений. Это сохраняет вывод "нормальным" даже в режиме raw. (Это не влияет на то, будет ли ввод автоматически эхом или нет; это контролируется ECHO
cflag в new_settings[3]
.)
Наконец, когда устанавливаются новые атрибуты, вызов будет успешным, если любой из новых параметров был установлен. Если параметры чувствительны - например, если вы запрашиваете пароль в командной строке - вам следует получить новые параметры и проверить правильность установки/снятия важных флагов, чтобы быть уверенным.
Если вы хотите увидеть текущие настройки терминала, выполните
stty -a
Флаги ввода обычно располагаются в четвертой строке, а флаги вывода - в пятой, с -
перед именем флага, если флаг не установлен. Например, вывод может быть
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
На псевдотерминалах и устройствах USB TTY скорость передачи данных не имеет значения.
Если вы пишете сценарии Bash, которые хотят считывать, например, пароли, рассмотрите следующую идиому:
#!/bin/bash
trap 'stty sane ; stty '"$(stty -g)" EXIT
stty -echo -echonl -imaxbel -isig -icanon min 1 time 0
Ловушка EXIT
выполняется всякий раз, когда оболочка выходит. stty -g
считывает текущие настройки терминала в начале сценария, поэтому текущие настройки восстанавливаются при выходе из сценария автоматически. Вы даже можете прервать скрипт нажатием Ctrl+C, и он сделает то, что нужно. (В некоторых угловых случаях с сигналами я обнаружил, что терминал иногда застревает с необработанными/неканоническими настройками (требуя набирать reset
+ Enter вслепую на терминале), но выполнение stty sane
перед восстановлением фактических исходных настроек всегда помогало мне это исправить. Так что вот почему он там есть; своего рода дополнительная безопасность.)
Вы можете читать строки ввода (без эха на терминале), используя read
встроенный bash, или даже читать ввод посимвольно, используя
IFS=$'\0'
input=""
while read -N 1 c ; do
[[ "$c" == "" || "$c" == $'\n' || "$c" == $'\r' ]] && break
input="$input$c"
done
Если вы не установите IFS
в ASCII NUL, read
встроенный будет потреблять разделители, так что c
будет пустым. Ловушка для молодых игроков.
Первый символ можно удалить с помощьюcut -c 2-
ls -l | tail -n+2 | cut -d" " -f1 | cut -c 2- | tr -d '\n'
ls
имеет очень плохие возможности форматирования. stat
имеет опцию --printf
для форматирования необходимой информации о файлах.
пример:
root@linux:~# stat --printf '%A\n' /etc/passwd /etc
-rw-r--r--
drwxr-xr-x
в случае тура, потому что вы не первый символ
root@linux:~# stat --printf '%A\n' file* | cut -c2-11 | tr -d '\n'