Всем нам удобно работать с единообразными интерфейсами. Любому человеку просто необходимо, чтобы одни и те же действия в разных программах выполнялись одинаковым образом. Только тогда пользователь сможет эффективно и безошибочно работать.
К сожалению, терминальные программы системы Linux стоят особняком от данного принципа. Причины кроются в закостенелой природе UNIX-терминалов и использовании принципов, считающимися нормой тридцать лет назад, но выглядящие дико с позиции современного пользователя. Примером такой дикости могут служить самобытные сочетания клавиш, предназначенные для рутиных действий. Например, многие красноглазые друзья считают, что использовать для копирования и вставки текста традиционные клавиши Ctrl+C/Ctrl+V в терминальных программах невозможно. В данной статье будет рассказано, как все-таки заставить работать эти привычные сочетания клавиш.
Вначале стоит объяснить, почему некоторые сочетания клавиши Ctrl+буква "заняты" как в чистой консоли, так и в программах эмуляции терминала (XTerm, Konsole, Gnome-trminal). Все дело в том, что UNIX-терминал должен иметь возможность отсылать командной оболочке так называемые "сигналы". Эти сигналы командная оболочка использует для управления процессами.
Для отправки сигналов, в эмуляторе терминала используются понятие "действие". Выполнение действий привязано к получению управляющих символов. А управляющие символы - это как раз и есть комбинация клавиши Ctrl и английской буквы (или знаков препинания). Вот какие действия обычно поддерживает любой Linux-терминал:
- intr - Послать сигнал прерывания текущего процесса
- quit - Послать сигнал выхода
- erase - Удалить последний введенный символ
- kill - Удалить текущую строку
- eof - Послать конец файла (завершить ввод)
- eol - Конец строки
- eol2 - Альтернативный символ конца строки (Не-POSIX)
- swtch - Переключиться на другой уровень оболочки (Не-POSIX)
- start - Запустить вывод далее, если он был приостановлен
- stop - Приостановить вывод
- susp - Послать сигнал остановки терминала
- dsusp - Послать сигнал остановки терминала после сброса ввода (Не-POSIX)
- rprnt - Перерисовать текущую строку (Не-POSIX)
- werase - Удалить последнее введенное слово (Не-POSIX)
- lnext - Протолкнуть в ввод следующий символ, даже если это специальный символ
Чтобы узнать, к каким комбинациям клавиш привязаны действия, можно дать команду:
$ stty -a
Сия команда выведет на консоль примерно такой текст:
speed 38400 baud; rows 35; columns 110; line = 0;
intr = ^C; quit = ^; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; swtch = ; 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
из него видно, что заняты комбинации клавиш Ctrl+C (^C) и Ctrl+V (^V) (а Ctrl+X, кстати, свободна).
Если писать точнее, то мы видим, что:
- При нажатии комбинации Ctrl+C происходит действие intr. Действие intr означает отправку сигнала SIGINT, который сигнализирует консольной программе что нужно завершить работу.
- При нажатии комбинации Ctrl+V происходит действие lnext. Это действие означает, что следующий вводимый символ надо воспринимать как символ, а не как команду (Последовательное нажатие Ctrl+V и Ctrl+A протолкнет в терминал символ ^A, имеющий код 01h).
Что мы должны сделать в такой ситуации, чтобы освободить Ctrl+C и Ctrl+V? Вначале подумать, а нужны ли нам действия intr и lnext. Действие intr вроде как нужно. А возникала ли у вас хоть раз необходимость в действии lnext? Нет? Значит, ненужно.
Почитав невнятную документацию по программе stty, кое-как можно понять, что для освобождения комбинации Ctrl+V нужно просто удалить привязанное к комбинации действие lnext. Для этого нужно воспользоваться командой:
$ stty lnext undef
Теперь осталось решить, что делать с intr (Ctrl+C). Однозначно, данное действие нам нужно, и его надо перевешивать на другую комбинацию клавиш. Но на какую? Разумно было бы перевесить на Ctrl+Break. Но как обозначается Break, в документации по stty не написано. Так же не написано, может ли вообще stty работать с не-ASCII символами (коды 0-127). Да и вообще непонятно, работает ли stty с символами или с кодами клавиш. Судя по всему, stty оперирует именно абстракцией "символ". А если так, то символа Break нет как понятия (хотя, возможно, есть символ Pause, который как раз завязан на ту же клавишу, но тут нужно разбираться).
Поэтому, пока никто не подсказал решения для Ctrl+Break, перевесим Ctrl+C на другое сочетание. Вначале хотел перевесить на Ctrl+0 (Ctrl+Ноль), но оказалось, что stty этого сделать не может. Вообще, создается впечатление, что stty может перенастраиваться только на 26 букв латинского алфавита (и, возможно на некоторые знаки препинания), которые можно задавать десятичными кодами 1-26. Например, команда "stty intr 10" задаст для действия intr комбинацию Ctrl+J, т.к. буква J - десятая буква в латинском алфавите. Если задавать коды не из диапазона 1-26, терминал начинает неимоверно глючить.
Чтоб было легче запомнить, настроим действие intr на комбинацию Ctrl+Q (ибо Q - это "quit", "выход").
Обратите внимание, что команда "stty -a" показывает, что Ctrl+Q сопоставлено с действием start. Это действие вызывает команду "продолжать вывод на теринал после приостановки вывода" (приостановка вывода делается с помощью действия stop клавишами Ctrl+S). Учитывая современные скорости выстреливания символов в поток вывода, нам действия start/stop не нужны. Так что спокойно будем использовать комбинацию Ctrl+Q как нам заблагорассудится.
Сначала нужно освободить комбинацию клавиш Ctrl+Q от действия start
$ stty start undef
Затем выполняем команду:
$ stty intr ^Q
и после нее, сразу, прерывание программы будет работать по Ctrl+Q (вместо традиционного для консоли Ctrl+C). Работу комбинации Ctrl+Q можно проверить на любой подходящей программе cat, top, и т.д.
Теперь команды:
stty lnext undef
stty start undef
stty intr ^Q
можно поместить в файл .bashrc в домашней директории пользователя, и терминал с этого момента будет запускаться со свободными комбинациями Ctrl+C и Ctrl+V.
Однако, есть еще одна тонкость, которую следуте учитывать. Дело в том, что в Linux есть как интерактивные, так и неинтерактивные терминалы. В неинтерактивных терминалах команда stty будет генерировать в стандартный вывод предупреждения:
stty: 'стандартный ввод': Неприменимый к данному устройству ioctl
Другими словами, данные команды не будут иметь эффекта, да и по большому счету не ненужны, например, когда bash-скрипт вызывается откуда-нибудь из crond или как часть автоматизации git. Чтобы этого не происходило, вызовы stty в файле .bashrc правильнее будет обернуть в следующее условие:
if [[ $- == *i* ]]; then
stty lnext undef
stty start undef
stty intr ^Q
fi
После такой настройки терминала можно в программе-эмулятора терминала (XTerm, Konsole, Gnome-trminal, ...) настроить комбинацию Ctrl+C для копирования в буфер, и Ctrl+V для вставки из буфера. И эти комбинации будут работать!