Реализация Bash read
позволяет вам указать максимальное количество символов для чтения, используя флаг -n
, или альтернативный символ завершения, используя флаг -d
. Ни одна из этих опций не будет работать со стандартным вводом терминала, потому что обычно драйвер терминала сохраняет ввод в своем собственном внутреннем буфере, пока пользователь не введет клавишу ENTER (или некоторые другие нажатия клавиш, такие как Control-C или Control-D).
Идея, к примеру, read -n1 char
состоит в том, что вы хотите, чтобы read
возвращал, как только пользователь вводит один символ, а не так, что вы хотите, чтобы read
ждал, пока пользователь наберет полный текст. строка, а затем вернуть первый символ этой строки. Аналогично, команда read -d';' command
должна вернуться, как только пользователь введет точку с запятой; опять же, ожидание, когда пользователь наберет полную строку, а затем просто возврат ее части до точки с запятой, будет неожиданным.
Таким образом, чтобы эти опции работали должным образом, встроенная функция read
должна указать драйверу терминала возвращать символы, как только они будут набраны. Если устройство ввода - это терминал, и вы указываете максимальную длину ввода или символ-разделитель, отличный от новой строки, read
переводит терминал в «сырой» режим, изменяя следующие termios flags :
off: ICANON INLCR OCRNL ONOCR ONLRET
on: ISIG IEXTEN ICRNL OPOST ONLCR
При выключенном ICANON
драйвер терминала больше не буферизует ввод.
Как отмечалось в исходном посте, драйвер ядра Linux использует входной буфер 4096 фиксированной длины для реализации редактирования строки и просто игнорирует напечатанные символы, которые не помещаются в этот буфер. Таким образом, если терминал находится в обычном режиме ввода, ваш ввод будет обрезан после 4096 символов. При выключенном ICANON
драйвер пропускает символы как можно быстрее, а ввод не обрезается.
Но побочным эффектом отключения канонизации ввода является то, что драйвер терминала больше не интерпретирует клавиши возврата и удаления, что делает невозможным редактирование строки. Вы можете попробовать это:
# I typed a, x, backspace, b, return
$ read -n 4 input
ax^?b
$ printf "%s" "$input" | hd
00000000 61 62 7f 78 |ab.x|
00000004
Обратите внимание, что символ удаления, отправленный клавишей возврата (0x7f
), сохраняется во вводе.
Это не идеальный пользовательский опыт; Вы, конечно, не захотите этого для ввода длинных входных данных. В большинстве случаев люди ожидают, что backspace будет «работать». Тем не менее, он идеально подходит для написания небольших консольных игр, где сценарий должен реагировать на каждое нажатие клавиши при вводе.
Bash сам использует библиотеку readline
для чтения ввода. readline
также переводит терминал в режим raw, но в отличие от встроенного read
, он фактически обрабатывает символы возврата, клавиши со стрелками и большой список других символов, включая множество специальных символов, о которых драйвер ядра, очевидно, ничего не знает, например, завершение вкладки и поиск в истории.
Встроенный read
также имеет флаг -e
, который заставляет его использовать readline
(если он читает с терминала). Выполнение вышеуказанного эксперимента с -e
дает, возможно, более удобные результаты:
# I typed a, x, backspace, b, c, d
$ read -en4 input
abcd
$ printf "%s" "$input" | hd
00000000 61 62 63 64 |abcd|
00000004
На этот раз readline
обработал клавишу Backspace, и read
вернулось после того, как были напечатаны четыре "настоящих" символа.