bash: чтение сбрасывает ввод строки терминала после 4096 байт - PullRequest
0 голосов
/ 10 сентября 2018

Чтобы продемонстрировать эту проблему, вставьте длинную строку (> 4096 байт) после выполнения этой команды в Linux:

read foo && wc -c <<<"$foo"

Результат - 4096, что означает усечение ввода.

Некоторые исследования показали, что размер буфера терминальной линии жестко задан в 4096, что объясняет усечение. Однако, когда я пытался читать с параметром -n, он работал:

read -n 32768 foo && wc -c <<<"$foo"

Результатом является фактическая длина ввода (+1, но это из-за строки здесь), а не 4096.

Так что я хотел бы знать, что такое магия с опцией -n 32768. Я не нашел соответствующей информации на странице руководства bash по этому поводу. На эту функцию мы можем положиться?

Ответы [ 2 ]

0 голосов
/ 10 сентября 2018

Реализация 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 вернулось после того, как были напечатаны четыре "настоящих" символа.

0 голосов
/ 10 сентября 2018

Из раздела man-страницы bash:

-n nchars

read возвращает после чтения символов nchars, а не ожидания полной строки ввода, но учитывает разделитель, если перед разделителем читается меньше символов nchars.

(Я почти уверен, что это расширение, специфичное для bash, а не то, на что вы можете положиться, если используете другие оболочки, если только вы не убедитесь, что конкретная оболочка также поддерживает его). Редактировать: zsh, например, делает что-то совсем другое с -n.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...