Как-то раз я спросила у пользователей Mastodon, что их не устраивает в работе с терминалом, и одним из ярких замечаний оказалось «редактирование уже введённой команды».
Мне эта проблема тоже реально знакома. Несмотря на то, что ввод текста и его редактирование является «базовой» задачей, мне потребовалось около 15 лет каждодневной работы с терминалом, чтобы привыкнуть к использованию Ctrl+A
для перехода к началу строки (или Ctrl+E
для перехода в конец — я использовала вместо этого Home
/End
).
Так что сегодня речь пойдёт о том, что ввод текста порой вызывает сложности. Я также поделюсь с вами кое-какими советами, которые сама была бы рада услышать давно.
Несогласованность между разными программами
В значительной степени ввод текста в терминале усложняет то, что разные программы обрабатывают его по-разному. Например:
- Некоторые программы (
cat
,nc
,git commit --interactive
и так далее) не поддерживают использование стрелок. Если вы будете нажимать в них соответствующие клавиши, то увидите лишь^[[D^[[D^[[C^[[C^
. - Многие программы (вроде
irb
,python3
в Linux и другие) используют библиотекуreadline
, которая предоставляет богатую базовую функциональность (просмотр истории, клавиши стрелок и так далее). - Некоторые программы (например,
/usr/bin/python3
на моём Mac) поддерживают самые простые возможности ввода вроде использования стрелок, но не другие вродеCtrl+влево
или обратного поиска с помощьюCtrl+R
. - В некоторых программах (вроде оболочки
fish
,ipython3
,micro
илиvim
) реализована собственная продуманная система ввода, специализированная конкретно под них.
Так что вариаций получается множество, и, пожалуй, стоит проговорить каждый пункт подробнее.
Пункт 1: основа
Во-первых, есть «основа» — что происходит, если программа просто получает текст через вызов fgets()
или другую функцию и больше никак не стремится повысить удобство в работе. Для меня использование подобных инструментов обычно выглядит так — если я запускаю установленную на моей машине версию dash
(довольно минималистичную оболочку) и нажимаю стрелку влево, то она просто выводит в терминал ^[[D
.
$ ls l-^[[D^[[D^[[D
Поначалу не кажется, что все эти инструменты «базового» уровня имеют много общего, но по факту есть несколько функциональных особенностей, которые вы получаете автоматически просто от самого терминала без какой-либо помощи со стороны программы.
Что же это за возможности:
- Естественно, ввод текста.
- Обратное перемещение курсора.
Ctrl+W
для удаления предыдущего слова.Ctrl+U
для удаления всей строки.- Ещё несколько возможностей, не связанных с редактированием текста (например,
Ctrl+C
для прерывания процесса,Ctrl+Z
для его приостановки и так далее).
Такие возможности нельзя назвать крутыми, но их наличие означает, что если вы хотите удалить слово, то обычно можете сделать это с помощью Ctrl+W
вместо того, чтобы 15 раз нажимать возврат, даже если работаете в среде, которая не предлагает никакой функциональности. Список всех поддерживаемых вашим терминалом комбинаций с клавишей Ctrl
можно вывести командой stty -a
.
Пункт 2: инструменты, которые используют readline
Следующей группой идут инструменты, использующие readline
. Readline — это широко используемая библиотека GNU, которая позволяет сделать текст более опрятным. Вот мои любимые комбинации, которые предоставляет эта библиотека:
Ctrl+E
(илиEnd
) для перехода в конец строки.Ctrl+A
(илиHome
) для перехода в начало строки.Ctrl+влево
/вправо
для перемещения вперёд/назад на 1 слово.Стрелка вверх
для возврата к предыдущей команде.Ctrl+R
для поиска по истории.
При этом вы также можете использовать Ctrl+W
/Ctrl+U
из «базового» списка, хотя Ctrl+U
вместо удаления всей строки удаляет только текст от курсора и до начала строки. Думаю, что Ctrl+W
также может несколько иначе трактовать понятие «слова».
Есть и многие другие комбинации (вот полный список). Я же перечислила только те, которые часто использую сама.
Наиболее известным инструментом, использующим readline
, наверняка является оболочка bash
(когда вы используете Ctrl+R
для поиска по истории в bash
, то эту возможность по факту предоставляет readline
), но есть и много других программ — например, psql
, irb
, python3
и так далее.
▍ Совет: можно использовать readline из любого инструмента с помощью rlwrap
Одним из самых приятных моментов я нахожу то, что если у вас есть программа вроде nc
без поддержки readline
, то вы можете просто выполнить rlwrap nc
, чтобы эту поддержку в неё встроить.
Это невероятная возможность, которая делает многие почти непригодные для использования инструменты более удобными в работе. Можно даже включить в rlwrap
собственные варианты автозавершения, хотя я никогда этого не пробовала.
Почему инструменты могут не использовать readline
Думаю, что поддержка readline
может отсутствовать в инструментах по следующим причинам:
- Программа очень проста (например,
cat
илиnc
) и мейнтейнеры, возможно, не хотят привносить в неё относительно крупную зависимость. - Нюансы лицензирования. Если лицензия программы не GPL-совместима —
readline
имеет лицензию GPL, а не LGPL. - Интерактивной является лишь небольшая часть программы, в связи с чем поддержку
readline
могли не счесть как значимый элемент. Например, вgit
немного интерактивных функций (таких какgit add -p
), и обычно вы просто вводите один символ вродеy
илиn
— если же требуется ввести что-то более значительное,git
переносит вас в текстовый редактор.
Например, в idris2
говорят, что не задействуют readline
ради сокращения числа зависимостей и для получения более интерактивных возможностей предлагают использовать rlwrap
.
Как понять, используете ли вы readline
Простейший тест, какой мне приходит на ум, это нажатие Ctrl+R
. Если вы увидите:
(reverse-i-search)`':
Значит, наверняка используете readline
. Естественно, это не 100%-метод (некоторые иные библиотеки тоже могут использовать термин reverse-i-search
), но я не знаю другую систему, которая бы задействовала его для поиска по истории.
Комбинации клавиш readline взяты из Emacs
Поскольку я пользуюсь vim
, то далеко не сразу поняла, откуда взяты эти комбинации (почему Ctrl+A
используется для перехода в начало строки??? Очень странно).
Как понимаю я, эти привязки были позаимствованы из Emacs — Ctrl+A
и Ctrl+E
делают в Emacs то же, что и в Readline. Так что предполагаю, что и многие другие комбинации между ними тоже совпадут, хотя я пробовала Ctrl+W
и Ctrl+U
в Emacs, и в нём их действия отличаются от действий в терминале. В общем, отличия всё же есть.
Подробнее об истории проекта Readline можно почитать здесь.
Пункт 3: другая библиотека ввода (вроде libedit)
На моём ноутбуке с Mac /usr/bin/python3
работает странным половинчатым образом, поддерживая лишь часть возможностей readline
(например, клавиши стрелок). Скажем, при нажатии Ctrl+влево
программа выводит ;5D
:
$ python3
>>> importt subprocess;5D
Разобраться с этим вопросом мне помогли ребята с Mastodon. Оказывается, в предустановленном на Mac дистрибутиве Python модуль readline
по факту заменён модулем libedit
, который представляет аналогичную библиотеку, но с менее широкой функциональностью. Причина может заключаться в том, что Readline имеет лицензию GPL.
В конечном итоге выяснить, что в моей версии Python используется libedit
, я смогла так:
$ python3 -c "import readline; print(readline.__doc__)"
Importing this module enables command line editing using libedit readline.
Тем не менее обычно Python использует именно readline
, если устанавливается под Linux или через Homebrew. Просто конкретная версия, которую инженеры Apple включают в свои системы, работает на libedit
.
Кроме того, в Python 3.13 собираются убрать зависимость от readline
, заменив её кастомной библиотекой, так что вскоре выражение «Python использует readline» перестанет быть актуальным.
Предполагаю, что на Mac есть и другие программы, которые используют libedit
, но я в этом не разбиралась.
Пункт 4: кастомное решение
К последней группе программ относятся те, в которых есть собственная (а иногда и намного более изощрённая) система редактирования текста. К ним относятся:
- Большинство редакторов текста для терминала (
nano
,micro
,vim
,emacs
и так далее). - Некоторые оболочки вроде
fish
. Например,fish
позволяет при вводе команды использовать отмену через нажатиеCtrl+Z
. Вzsh
задействуется текстовый редакторzle
. - Некоторые REPL вроде
ipython
, в котором вместоreadline
используется библиотекаprompt_toolkit
. - Многие другие программы (вроде
atuin
).
Вот некоторые из их особенностей:
- Более эффективное автозавершение, подстроенное под сам инструмент.
- Более удобное управление историей (например, с выделением синтаксиса) в сравнении с тем, которое по умолчанию предоставляет
readline
. - Больше всевозможных комбинаций клавиш.
▍ Кастомные системы ввода зачастую основаны на readline
Я решила посмотреть, как обрабатывает ввод Atuin (прекрасный инструмент для поиска по истории оболочки, которым я начала пользоваться недавно). Если взглянуть на код и его обсуждение, то становится ясно, что хоть реализация этого инструмента и кастомная, основана она именно на readline
. На мой взгляд, это разумно, так как многие пользователи привыкли к используемым в ней комбинациям клавиш, и это обеспечивает для них дополнительное удобство при работе.
Аналогично дела обстоят с prompt_toolkit
(библиотекой, которую использует IPython) — фактически она поддерживает множество опций (включая привязки клавиш в стиле vi
), но по умолчанию в ней используются привязки именно как в readline
.
И есть немало программ, которые поддерживают самые простые привязки клавиш vim
(вроде j
для «вниз» и k
для «вверх»). Например, Fastmail поддерживает j
и k
, несмотря на то, что основная часть остальных его привязок не имеют отношения к vim
.
Предполагаю, что большинство кастомных систем ввода на основе readline
имеют различные тонкие расхождения с этой библиотекой, но меня это не волнует, поскольку я крайне плохо разбираюсь в большинстве возможностей readline
. Я использую всего где-то 5 комбинаций, поэтому до тех пор, пока такие системы поддерживают 5 базовых известных мне команд (которые в них есть всегда), меня это вполне устраивает. Причём эти кастомные системы зачастую предоставляют намного более качественное автозавершение, чем можно получить при использовании только readline
, поэтому обычно я предпочитаю именно их.
Многие оболочки поддерживают комбинации клавиш из редактора vi
Bash
, zsh
и fish
предлагают для ввода текста «режим vi». В очень ненаучном опросе, который я провела на Mastodon, 12% людей сказали, что используют именно его. Так что, похоже, этот режим довольно популярен.
В readline
тоже есть «режим vi» (таким образом реализована её поддержка в Bash), то есть по аналогии эта поддержка есть и во многих других программах.
Я всегда считала, что режим vi
очень крут, но хоть я и пользуюсь vim
, почему-то его так и не освоила.
Важно понимать ситуацию
Я долгие годы недоумевала, почему используемое мной приложение командной строки не работает так, как я бы хотела. Поэтому хорошо, когда можешь в той или иной степени понять, с чем имеешь дело.
Думаю, что при вводе текста в командную строку я размышляю примерно так:
- Работают ли клавиши стрелок? Возможно, системы ввода нет совсем, но я по крайней мере могу использовать
Ctrl+W
иCtrl+U
, а также дополнить инструмент с помощьюrlwrap
, если мне потребуется больше возможностей. - Будет ли
Ctrl+R
выводитьreverse-i-search
? Если да, то это навернякаreadline
, и тогда я смогу использовать все привычные мне комбинации, а также выводить какую-то базовую историю и получать предыдущую команду нажатием клавишивверх
. - Делает ли
Ctrl+R
что-то другое? В этом случае, скорее всего, используется кастомная библиотека, которая будет работать более-менее схожим сreadline
образом. При этом, если я захочу узнать, как конкретно она работает, то всегда могу заглянуть в документацию.
Возможность подобным образом проверить, что конкретно происходит при использовании разных комбинаций, делает командную строку более предсказуемой и менее хаотичной.
О чём в этом посте не говорилось
При вводе текста существует и много других сложностей, о которых я не сказала. Например:
- проблемы, связанные с
ssh
/tmux
/ и прочим, - переменная среды
TERM
, - то, что разные терминалы (
gnome
,iTerm
,xterm
и прочие) по-разному реализуют копирование/вставку текста, - Юникод,
- и другие.
Автор: Bright_Translate