33 ctrl-комбинации в терминале: как Ascii определяет настоящие шорткаты

В терминале всего 33 Ctrl-комбинации, и это не условность, а прямое следствие устройства ASCII. Всё, что мы привыкли считать "шорткатами" - Ctrl-C, Ctrl-D, Ctrl-Z, Ctrl-W и прочие - в реальности просто разные варианты ввода управляющих символов. И именно поэтому Ctrl-1 или Ctrl-Shift-A терминал "не понимает": таких control-кодов просто не существует.

---

Откуда берутся те самые 33 Ctrl-кода

Исторически терминал опирается на таблицу ASCII. В ней есть 33 так называемых управляющих символа - байты с кодами от 0 до 31 и 127. Для них зарезервированы сочетания с Ctrl:

- буквы A-Z → 26 кодов,
- плюс ещё 7 символов: `@`, `[`, ``, `]`, `^`, `_`, `?`.

Вот почему Ctrl можно "зажать" осмысленно только в этих 33 случаях. Любые другие Ctrl-комбинации:

- либо игнорируются терминалом и проходят как обычные символы,
- либо превращаются в ANSI escape-последовательности (например, стрелки, F-клавиши и т.п.).

По этой же причине:

- Ctrl-1 не даёт отдельного управляющего кода - терминал видит просто "1".
- Ctrl-3 часто совпадает с Ctrl-[, потому что их коды мапятся друг на друга.
- Комбинации вроде Ctrl+Shift+C обрабатываются не ядром и даже не shell'ом, а самим эмулятором терминала - это не часть набора ASCII control characters.

---

Почему Ctrl-M = Enter, а Ctrl-I = Tab

Самое "магическое" место - совпадение некоторых Ctrl-комбинаций с привычными клавишами:

- Ctrl-M даёт код `CR` (carriage return), который исторически и есть "перевод каретки", то есть Enter.
- Ctrl-I даёт код `HT` (horizontal tab), который терминал трактует как Tab.

В итоге:

- Нажатие Enter и Ctrl-M отправляют один и тот же байт.
- Нажатие Tab и Ctrl-I - тоже один и тот же управляющий символ.

Поэтому использовать Ctrl-M и Ctrl-I как отдельные шорткаты в терминальном приложении - почти всегда плохая идея: вы перетираете стандартное поведение клавиш ввода и табуляции. Если очень хочется, придётся настраивать эмулятор терминала так, чтобы он различал эти сочетания и переназначал их до того, как данные попадут в TTY.

---

Почему "официальные" имена управляющих кодов бесполезны

Каждому управляющему байту в ASCII когда-то дали имя: например, байт 3 - это ETX (End of Text). Эти обозначения придумали для телеграфных систем, задолго до массовых терминалов и современных ОС.

Со временем, когда эти же байты перекочевали в Unix-терминалы, примерно половина из них сменила назначение. В результате:

- формальные ASCII-имена всё ещё существуют,
- но на практике чаще всего не совпадают с их сегодняшним поведением в терминале,
- поэтому в прикладной работе ими почти никто не пользуется - легче ориентироваться по сочетанию клавиш и эффекту, а не по имени кода.

---

Где именно обрабатываются Ctrl-коды

Главный сюрприз в том, что разные Ctrl-сочетания "живут" в разных слоях:

1. В драйвере терминала в ОС
Например, классические Ctrl-C, Ctrl-Z, Ctrl- по умолчанию превращаются в сигналы (`SIGINT`, `SIGTSTP`, `SIGQUIT`).
Сюда же часто попадают редактирующие коды типа Backspace, Ctrl-W, Ctrl-U - но только в определённом режиме.

2. В библиотеках ввода (например, readline)
Эти библиотеки интерпретируют управление курсором, историю, редактирование строки: Ctrl-A (в начало строки), Ctrl-E (в конец), Ctrl-R (поиск по истории) и т.п.
Здесь уже нет "магии ОС" - это уровень приложения или библиотеки, которые получили сырые байты и сами решают, как реагировать.

3. В самом приложении
Программы вроде текстовых редакторов, интерфейсов поверх curses или tui-фреймворков настраивают собственные реакции на каждый control-код: Ctrl-S, Ctrl-Q, Ctrl-F и т.д.

Жёсткой логики в том, какой код кем именно обрабатывается, нет. Это результат многослойной исторической эволюции Unix и терминалов.

---

Canonical vs noncanonical: построчный и посимвольный ввод

То, как обрабатываются некоторые Ctrl-коды (и даже Backspace), зависит от режима терминала.

Canonical mode (канонический, построчный ввод)

- ОС сама буферизует ввод до нажатия Enter.
- Редактирование строки (Backspace, Ctrl-W, Ctrl-U и т.п.) происходит на уровне драйвера терминала.
- Приложение получает уже готовую строку, когда пользователь нажал Enter.

В этом режиме:

- Ctrl-W удаляет последнее слово,
- Ctrl-U очищает строку целиком,
- Backspace стирает последний символ - и всё это делает не приложение, а терминальный драйвер в ядре.

Так работают классические утилиты командной строки, которые "думают" построчно: оболочки, простые консольные программы.

Noncanonical mode (неканонический, посимвольный ввод)

- Приложение получает каждый нажатый символ сразу.
- Никакой встроенной "магии редактирования" от ОС: Ctrl-W, Ctrl-U и Backspace попадают в программу как байты, и дальше всё зависит от логики приложения.

Этот режим используют:

- полноэкранные редакторы (vim, nano и подобные),
- TUI-приложения,
- программы, которым важен каждый символ и точное время его ввода.

Режимы, в которых работает программа, можно увидеть через системный вызов `ioctl`, который настраивает параметры терминала. При трассировке системных вызовов удобно наблюдать, когда приложение включает неканонический или "сырой" режим.

---

Настройка Ctrl-кодов через stty

Все те коды, которые обрабатывает драйвер терминала (включая Backspace и сочетания для сигналов), настраиваются утилитой `stty`. С её помощью можно:

- изменить клавишу, которая посылает `SIGINT` (обычно Ctrl-C),
- переназначить код, который останавливает вывод (Ctrl-S) или возобновляет его (Ctrl-Q),
- поменять поведение символов для редактирования строки (Erase, Kill и т.д.).

Текущие настройки выводятся через `stty -a`.

Теоретически это открывает массу возможностей: можно подогнать терминал под раскладку, привычки или особенности оборудования. На практике переназначение базового поведения Ctrl-C, Backspace и прочих часто приводит к хаосу, особенно если работать на нескольких серверах или в чужих окружениях.

---

Можно ли отключить сигналы?

Да. Сигналы, генерируемые Ctrl-комбинациями, не "вшиты" навсегда. Те же:

- Ctrl-C → `SIGINT`,
- Ctrl-Z → `SIGTSTP`,
- Ctrl- → `SIGQUIT`,

можно:

- изменить через `stty`,
- или вовсе отключить (убрать привязку к управляющему байту).

В результате Ctrl-C перестанет убивать процесс, а будет просто передаваться программе как обычный управляющий символ. Многие интерактивные приложения частично или полностью перехватывают эти коды, чтобы реагировать особым образом (например, показывать диалог подтверждения, а не завершаться сразу).

---

Ctrl+Shift+... и стрелки: другая лига

Комбинации наподобие:

- Ctrl+Shift+C / Ctrl+Shift+V,
- Ctrl+Shift+T (новая вкладка в эмуляторе),
- Ctrl+стрелки,

не являются ASCII control-кодами. Здесь работают другие механизмы:

1. Комбинации уровня эмулятора терминала
Эмулятор "перехватывает" сочетание до того, как оно попадёт в TTY.
Так обычно реализованы копирование, вставка, открытие вкладок и прочие GUI-действия. Для TTY эти клавиши вообще не существуют - они не доходят до ядра.

2. ANSI escape-последовательности
Стрелки, F-клавиши, часто Ctrl+стрелки кодируются как последовательности вида `ESC [ ...` - это уже не одиночный байт, а несколько символов подряд. Приложение должно их распарсить и понять, что именно нажал пользователь.

Пример: Ctrl+Left Arrow может выглядеть как последовательность `ESC [ 1 ; 5 D` (но точный код зависит от эмулятора и настроек).

---

Backspace, "другой" Backspace и историческая боль

Одна из самых запутанных тем - Backspace. На разных системах он может:

- слать код `BS` (0x08),
- или `DEL` (0x7F),
- а иногда - что-то ещё, в зависимости от раскладки, терминала и настроек.

Для пользователя это проявляется просто: где-то Backspace стирает символ, а где-то печатает `^?` или `^H`, или вообще ведёт себя странно. Отсюда:

- тысячи конфигураций, костылей в `.inputrc`, `.bashrc`, настройках терминала,
- необходимость подстраивать поведение через `stty erase` или настройки эмулятора.

Проблема усугубляется тем, что в истории Unix и терминалов код удаления символа несколько раз "переезжал" между `BS` и `DEL`. Разные системы и программы остановились на разных вариантах.

---

Почему на разных машинах всё по-разному

То, как ведут себя конкретные Ctrl-комбинации, - результат сочетания:

- настроек драйвера терминала (через `stty`),
- возможностей и настроек эмулятора терминала,
- используемой библиотеки ввода (readline, libedit и др.),
- конкретного приложения и его ключей запуска,
- особенностей ОС (Linux, BSD, macOS и т.д.).

Поэтому:

- одна и та же комбинация может посылать разные байты в разных эмуляторах,
- одни и те же коды могут по-разному интерпретироваться разными приложениями,
- шорткат, который "работает всегда" у одного пользователя, на другой машине внезапно не срабатывает или даёт неожиданный эффект.

---

А нужно ли вообще в этом разбираться?

Строго говоря, нет. Большинству пользователей не нужно знать:

- какие именно байты соответствуют Ctrl-K или Ctrl-R,
- где именно обрабатывается Ctrl-W - в ОС или в приложении,
- чем именно canonical mode отличается от noncanonical.

С этим прекрасно можно жить, просто используя стандартные шорткаты оболочки, редактора и терминала.

Но если вы:

- пишете собственное терминальное приложение,
- отлаживаете сложное TUI,
- или хотите сознательно управлять поведением Ctrl-комбинаций,

то понимание того, что:

- существует всего 33 управляющих Ctrl-кода,
- не все они свободны для использования (часть занята под сигналы и системное редактирование),
- многие конфликты (например, с Ctrl-S или Ctrl-Q) исторически заложены в терминальный стек,

поможет избежать множества ловушек.

---

Практические рекомендации для авторов терминальных приложений

1. Не используйте Ctrl-I и Ctrl-M как горячие клавиши
Они совпадают с Tab и Enter. Пользователю будет сложно предсказать поведение, а вы рискуете сломать базовую навигацию и ввод.

2. С осторожностью используйте Ctrl-S и Ctrl-Q
Исторически эти коды управляли остановкой и возобновлением вывода (XON/XOFF). На многих системах это поведение сохранилось, и пользователь внезапно "подвиснет" в терминале.

3. Проверяйте режим ввода
В неканоническом режиме все редактирующие коды поступают в приложение напрямую, и вы должны явно решать, как на них реагировать.

4. Не полагайтесь на специфичные Ctrl-коды без альтернативы
Для важных операций лучше иметь дублирующий шорткат или команду, доступную через интерфейс меню, командную строку внутри приложения или функциональные клавиши.

5. Учитывайте, что пользователи могут менять stty
Если приложение критически зависит от конкретного поведения Ctrl-C или Backspace, стоит на старте явно проверять и при необходимости корректировать параметры терминала - или хотя бы документировать требования.

---

Итог

В терминале реально существуют только 33 осмысленные Ctrl-комбинации, соответствующие ASCII control characters. Всё остальное:

- либо проходит как обычный текстовый ввод,
- либо превращается в escape-последовательности,
- либо обрабатывается "на лету" самим эмулятором терминала и никогда не попадает в TTY.

Понимание того, где живёт каждый код - в драйвере терминала, библиотеке ввода или приложении, - объясняет, почему Ctrl-M ведёт себя как Enter, Ctrl-I - как Tab, почему Ctrl-1 "не существует", а Backspace может годами сводить с ума администраторов и разработчиков.

Прокрутить вверх